Linq to Entities - Projections against Query Syntax vs Method Syntax - c#

Since LINQ query expression are translated "under the covers" to call the same methods that a corresponding method query would call (at least I think so), I would expect these two queries to return the same type. For some reason though this:
var result = from i in db.Invoices
select new { i.InvoiceNum };
sets result as an IQueryable<'a> with each member having an InvoiceNum property, while this
IQueryable<string> result2 = db.Invoices.Select(i => i.InvoiceNum);
Is smart enough to return IQueryable<string> (obviously, since that compiles)
Clearly one of my assumptions is wrong, and I was hoping an expert could help me understand a bit better.
(this is EF4, but the same happens with linq-to-objects, and I'm guessing the same would also happen with L2S)

When you write new { } you are creating an anonymous type
try
var result = from i in db.Invoices
select i.InvoiceNum;
and you will see that it returns the type you expect.

those are not the same, the first one is returning anonymousn type, to make them the same you need to have th first one as:
var result = from i in db.Invoices
select i.InvoiceNum;

In your first statement, you create an anonymous type with one property named "InvoiceNum". This happens because you use the new { } syntax. That anonymous type is not a String. The equivalent method syntax would be:
var result = db.Invoices.Select(i => new { i.InvoiceNum });

Related

C# and LINQ - arbitrary statement instead of let

Let's say I'm doing a LINQ query like this (this is LINQ to Objects, BTW):
var rows =
from t in totals
let name = Utilities.GetName(t)
orderby name
select t;
So the GetName method just calculates a display name from a Total object and is a decent use of the let keyword. But let's say I have another method, Utilities.Sum() that applies some math on a Total object and sets some properties on it. I can use let to achieve this, like so:
var rows =
from t in totals
let unused = Utilities.Sum(t)
select t;
The thing that is weird here, is that Utilities.Sum() has to return a value, even if I don't use it. Is there a way to use it inside a LINQ statement if it returns void? I obviously can't do something like this:
var rows =
from t in totals
Utilities.Sum(t)
select t;
PS - I know this is probably not good practice to call a method with side effects in a LINQ expression. Just trying to understand LINQ syntax completely.
No, there is no LINQ method that performs an Action on all of the items in the IEnumerable<T>. It was very specifically left out because the designers actively didn't want it to be in there.
Answering the question
No, but you could cheat by creating a Func which just calls the intended method and spits out a random return value, bool for example:
Func<Total, bool> dummy = (total) =>
{
Utilities.Sum(total);
return true;
};
var rows = from t in totals
let unused = dummy(t)
select t;
But this is not a good idea - it's not particularly readable.
The let statement behind the scenes
What the above query will translate to is something similar to this:
var rows = totals.Select(t => new { t, unused = dummy(t) })
.Select(x => x.t);
So another option if you want to use method-syntax instead of query-syntax, what you could do is:
var rows = totals.Select(t =>
{
Utilities.Sum(t);
return t;
});
A little better, but still abusing LINQ.
... but what you should do
But I really see no reason not to just simply loop around totals separately:
foreach (var t in totals)
Utilities.Sum(t);
You should download the "Interactive Extensions" (NuGet Ix-Main) from Microsoft's Reactive Extensions team. It has a load of useful extensions. It'll let you do this:
var rows =
from t in totals.Do(x => Utilities.Sum(x))
select t;
It's there to allow side-effects on a traversed enumerable.
Please, read my comment to the question. The simplest way to achieve such of functionality is to use query like this:
var rows = from t in totals
group t by t.name into grp
select new
{
Name = t.Key,
Sum = grp.Sum()
};
Above query returns IEnumerable object.
For further information, please see: 101 LINQ Samples

Can I Use a Delegate Variable in a Join and Get an IQueryable

Origin
I have a LINQ-to-SQL query that will vary slightly based on external criteria. I wrote a switch statement for the three different cases, but the series of queries in the different cases are nearly identical. The only thing that differs is the delegate used for the join condition.
So, in order to make my code DRYer, I decided to just use the switch statement to select from three different delegates. I would then save the chosen delagate in a variable, and use that variable in the .Join call.
Problem
However, when I use the variable instead of explicitly typing out the delegate, the .Join no longer returns an IQueryable but instead returns an IEnumerable. I should clarify that I'm getting the return value from Intellisense on the 'var' keyword.
So this one returns an IQueryable:
var dc = new CloudDataContext();
var manifests = dc.ShippingManifests.Join(
dc.Locations,
man => man.OriginId,
loc => loc.Id,
(man, loc) => man
);
But this one returns an IEnumerable:
protected static int? ManifestOriginId( ShippingManifest manifest ) {
return manifest.OriginId;
}
Func<ShippingManifest, int?> originJoiner = GridModelManifests.ManifestOriginId;
var dc = new CloudDataContext();
var manifests = dc.ShippingManifests.Join(
dc.Locations,
originJoiner,
loc => loc.Id,
(man, loc) => man
);
Question
I'm curious why this happens, but I'm more curious how I can accomplish what I'm trying to accomplish. I need to get an IQueryable, as the resulting query is going to be passed on down the line for further manipulation.
Try it as an Expression.
When it isn't, the query will probably be hydrated and pulled into memory prior to the join. What you're after is an Expression<Func<ShippingManifest, int?>>:
Expression<Func<ShippingManifest, int?>> originJoiner = man => man.OriginId;
This allows the expression visitor in Linq to SQL to continue processing it before it sends it off to the database.

LINQ What does 'select new' do?

I've built the following LINQ query
var activeMembers = from m in context.py3_membershipSet
join c in context.ContactSet on m.py3_Member.Id equals c.ContactId
where m.statuscode.Value == 1
orderby m.py3_name
select m;
But I've since seen an example formatted as such:
var contacts =
(
from c in xrm.ContactSet
join a in xrm.AccountSet on c.ParentCustomerId.Id equals a.Id
where a.Name == "Acme Pty Ltd"
select new
{
Name = c.FullName,
DOB = c.BirthDate,
Gender = (c.FormattedValues.Contains("gendercode") ? c.FormattedValues["gendercode"] : "Ambiguous")
}
);
(I realise this is a different set of data) What does the inclusion of the 'select new' actually do in this case?
What are the benefits of it over my example in the first code block?
I realise some might find this a tedious question, but I a want to learn LINQ and need to learn it fast. But I also don't want to run something -that I don't fully understand- on a clients live CRM
LINQ returns a collection of anonymous objects, either way. select new let's you define the layout of that object and which properties/property names are included in that anonymous object.
You can also use select new ClassName { } to return a list of instances of an entity class that you define.
As previous answers have noted both methods return an anonymous type. To fully answer your question though: "What are the benefits of the second statement over the first?"
The first statement returns all of the fields of m as-is. If you have 7 "columns" then activeMembers will contain all of them and whatever data they contain.
In the second statement you're projecting the results into a custom anonymous object that has only 3 fields. Not only that but you can perform logic on the "source fields" before storing them in the anonymous object. This gives you much of the flexibility of storing them in a container class without you actually having to define that class in code.
You could also do select new SomeClass { } which would project your results into a predefined class container.
The following pseudo-code may or may not be helpful in understanding the difference:
var myQuery = from p in someContext.someTable
group p by p.someField into g
select new MyContainer {
Field1 = g.Sum(a => a.field1)
Field2 = g.Max(a => a.field2)
};
myQuery in the above is now a collection of MyContainer. If I had omitted the class MyContainer then it would be a collection of an Anonymous Type containing the fields that I specified. Obviously, the difference here is that MyContainer has to be defined elsewhere in your code whereas the anonymous class is built/defined for you at compile time. In:
var myQuery = from p in someContext.someTable
select p;
myQuery is an anonymous class containing all of the fields and their values in someTable.
You are using the Queryable.Select<TSource, TResult>(IQueryable<TSource>, Expression<Func<TSource, TResult>>) method where ContactSet is the TSource and the of anonymous object return type is the TResult. Your code could also be written as
...Select(r => new {
Name = c.FullName,
DOB = c.BirthDate,
Gender = (c.FormattedValues.Contains("gendercode") ? c.FormattedValues["gendercode"] : "Ambiguous")
})
where the select method is returning a collection of anonymous types.
Also, there is a bit more going on under the hood since it looks like you're querying the database. Your implementation of IQueryable looks at the Select method that you've written, and translates the expression you've provided into valid SQL so that it can retrieve all the necessary information for the anonymous object that you're returning. Notice that I said your implemenation of IQuerable translates the provided expression (not function) into sql. The Select extension method only accepts expressions, not functions because it can't translate functions to sql.

NHibernate 3 LINQ - how to create a valid parameter for Average()

Say I have a very simple entity like this:
public class TestGuy
{
public virtual long Id {get;set;}
public virtual string City {get;set;}
public virtual int InterestingValue {get;set;}
public virtual int OtherValue {get;set;}
}
This contrived example object is mapped with NHibernate (using Fluent) and works fine.
Time to do some reporting. In this example, "testGuys" is an IQueryable with some criteria already applied.
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
This works just fine. In NHibernate Profiler I can see the correct SQL being generated, and the results are as expected.
Inspired by my success, I want to make it more flexible. I want to make it configurable so that the user can get the average of OtherValue as well as InterestingValue. Shouldn't be too hard, the argument to Average() seems to be a Func (since the values are ints in this case). Easy peasy. Can't I just create a method that returns a Func based on some condition and use that as an argument?
var fieldToAverageBy = GetAverageField(SomeEnum.Other);
private Func<TestGuy,int> GetAverageField(SomeEnum someCondition)
{
switch(someCondition)
{
case SomeEnum.Interesting:
return tg => tg.InterestingValue;
case SomeEnum.Other:
return tg => tg.OtherValue;
}
throw new InvalidOperationException("Not in my example!");
}
And then, elsewhere, I could just do this:
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(fieldToAverageBy) });
Well, I thought I could do that. However, when I do enumerate this, NHibernate throws a fit:
Object of type 'System.Linq.Expressions.ConstantExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
So I am guessing that behind the scenes, some conversion or casting or some such thing is going on that in the first case accepts my lambda, but in the second case makes into something NHibernate can't convert to SQL.
My question is hopefully simple - how can my GetAverageField function return something that will work as a parameter to Average() when NHibernate 3.0 LINQ support (the .Query() method) translates this to SQL?
Any suggestions welcome, thanks!
EDIT
Based on the comments from David B in his answer, I took a closer look at this. My assumption that Func would be the right return type was based on the intellisense I got for the Average() method. It seems to be based on the Enumerable type, not the Queryable one. That's strange.. Need to look a bit closer at stuff.
The GroupBy method has the following return signature:
IQueryable<IGrouping<string,TestGuy>>
That means it should give me an IQueryable, all right. However, I then move on to the next line:
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
If I check the intellisense for the g variable inside the new { } object definition, it is actually listed as being of type IGrouping - NOT IQueryable>. This is why the Average() method called is the Enumerable one, and why it won't accept the Expression parameter suggested by David B.
So somehow my group value has apparently lost it's status as an IQueryable somewhere.
Slightly interesting note:
I can change the Select to the following:
.Select(g => new { City = g.Key, Avg = g.AsQueryable<TestGuy>().Average(fieldToAverageBy) });
And now it compiles! Black magic! However, that doesn't solve the issue, as NHibernate now doesn't love me anymore and gives the following exception:
Could not parse expression '[-1].AsQueryable()': This overload of the method 'System.Linq.Queryable.AsQueryable' is currently not supported, but you can register your own parser if needed.
What baffles me is that this works when I give the lambda expression to the Average() method, but that I can't find a simple way to represent the same expression as an argument. I am obviously doing something wrong, but can't see what...!?
I am at my wits end. Help me, Jon Skeet, you're my only hope! ;)
You won't be able to call a "local" method within your lambda expression. If this were a simple non-nested clause, it would be relatively simple - you'd just need to change this:
private Func<TestGuy,int> GetAverageField(SomeEnum someCondition)
to this:
private Expression<Func<TestGuy,int>> GetAverageField(SomeEnum someCondition)
and then pass the result of the call into the relevant query method, e.g.
var results = query.Select(GetAverageField(fieldToAverageBy));
In this case, however, you'll need to build the whole expression tree up for the Select clause - the anonymous type creation expression, the extraction of the Key, and the extraction of the average field part. It's not going to be fun, to be honest. In particular, by the time you've built up your expression tree, that's not going to be statically typed in the same way as a normal query expression would be, due to the inability to express the anonymous type in a declaration.
If you're using .NET 4, dynamic typing may help you, although you'd pay the price of not having static typing any more, of course.
One option (horrible though it may be) would be try to use a sort of "template" of the anonymous type projection expression tree (e.g. always using a single property), and then build a copy of that expression tree, inserting the right expression instead. Again, it's not going to be fun.
Marc Gravell may be able to help more on this - it does sound like the kind of thing which should be possible, but I'm at a loss as to how to do it elegantly at the moment.
Eh? the parameter to Queryable.Average is not Func<T, U>. It's Expression<Func<T, U>>
The way to do this is:
private Expression<Func<TestGuy,int>> GetAverageExpr(SomeEnum someCondition)
{
switch(someCondition)
{
case SomeEnum.Interesting:
return tg => tg.InterestingValue;
case SomeEnum.Other:
return tg => tg.OtherValue;
}
throw new InvalidOperationException("Not in my example!");
}
Followed by:
Expression<Func<TestGuy, int>> averageExpr = GetAverageExpr(someCondition);
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(averageExpr) });

Trouble putting Statement Lambda inside a LINQ query

I'm trying to inject some inline work as a Statement Lambda into a LINQ query select like so...
// NOTE: mcontext.Gettype() == System.Data.Linq.DataContext
// Okay - compiles, nothing unusual
var qPeople1 = from ME.tblPeople person in mcontext.tblPeoples
select person;
// ERROR - see below compile Error - Can I retrofit this thing?
var qPeople2 = from ME.tblPeople person in mcontext.tblPeoples
select (() => {
return person;
})();
Error:
Error 2 Method name
expected file.cs 166 27 MigrationCore
... however I'd also be equally happy to see an Expression Lambda work inline first.
Note: I know the code sample is redundant in its effort but I'm looking for the basic concept. If it's workable I will be expanding it.
Query syntax requires a method reference - it won't accept a lambda and in your second example you're giving it a ME.tblPeople instance.
However, if you use the extension method syntax you can achieve this easily:
int i = 0;
var qPeople3 = (from ME.tblPeople person in mcontext.tblPeoples
select person).Select(person => { i += 1; return person; });
(I've added the incrementing integer as an example, though note that it doesn't actually change from zero until you've enumerated qPeople3.)
Addendum
This only works with LINQ to Objects. To use it with a LINQ to SQL query an AsEnumerable() call is required before the Select() call.
Notes
You don't actually need the from-in-select construct for this example, the snippets below are (AFAICT) identical, but I've left it in above for similarity to the earlier examples and to illustrate that it works. The second snippet splits the two statements into separate lines, also with identical results.
int i = 0;
var qPeople4 = mcontext.tblPeoples.Select<ME.tblPeople,ME.tblPeople>(person => { i += 1; return person; });
int i = 0;
var qPeople1 = from ME.tblPeople person in mcontext.tblPeoples
select person;
var qPeople5 = qPeople1.Select(person => { i += 1; return person; });
There are two kinds of lambda expressions: anonymous delegates and expression trees. The former kind is used by LINQ to Objects, and allows any valid anonymous method body. The latter kind is used by LINQ to SQL and requires that its body be a single expression. This expression is then passed into the L2SQL runtime and manipulated into the SQL sent to the server.
To perform your inline work, you will need to use two steps: 1) get SQL data with a valid select expression, then 2) manipulate that data as an IEnumerable<> with LINQ to Objects to do your inline work. That could look something like this:
var qPeople1 = from ME.tblPeople person in mcontext.tblPeoples
select person;
var i = 0;
var qPeople2 = qPeople1.AsEnumerable().Select(person => {
i += 1;
return person;
});

Categories

Resources