Lambda Expressions and Method calls - c#

Hi I have a collection of Objects in a Listview and i need to know if i can iterate through them with a lambda expression. and call a method on it in the expression.
Lets say i need to save a group of people to a database.
List<People> someList;
someList.Select(person => person.Save());
is this possible to do? so far i have not been able to get it working.
thanks

You can use the ForEach method of a generic list:
List<People> someList;
someList.ForEach(person => person.Save());

someList.ForEach(p => p.Save());

Sounds like you want a foreach statement:
foreach(People p in someList)
{
p.Save();
}
But if you really want to do it in lambda expressions and LINQ, then your problem with the above code is that .Select(...) returns an IEnumerable/IQueryable, which creates a new query but doesn't execute your lambda expressions.
You could force the lambda to evaluate by calling an extension method that forces an enumeration of the data the IEnumerable/IQueryable represents. For instance by doing:
someList.Select(person => person.Save()).Count();
but this also assumes your Save() method returns non-void.
Edit:
As others have pointed out, if you're working specifically with a List<>, then you can also do:
someList.ForEach(person => person.Save());

Related

Using Expression<Func<>> in a LINQ Query

I want to define a Func<ProductItemVendor, bool> filter expression named CompareProductItemVendorIds, which can be used throughout my application, mostly within Entity Framework/LINQ queries.
I've learned that in order to be able to use this filter in a LINQ query, I must declare it as Expression<Func<>> instead of just Func<>. I understand the reason for this, and it's easy for me to do this.
But I'm having the following problems using that expression in my queries.
First, code such as:
ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds)
Note: ProductItem is a database entity, and its ProductItemVendors property is a navigation collection.
Produces the error:
`Instance argument: cannot convert from 'System.Collections.Generic.ICollection' to 'System.Linq.IQueryable'
And second, code such as:
var results = from v in Repository.Query<ProductItemVendor>()
where CompareProductItemVendorIds(v)
select v;
Produces the error:
'CompareProductItemVendorIds' is a 'variable' but is used like a 'method'
So I have my nice shiny new Expression<Func<>>. How can I use it in my LINQ queries?
ProductItem is already an Entity, so you can't use your Expression, you need to use Compile() to get the Func<> from your Expression<Func<>> since ProductItemVendors is no longer an IQueryable
ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds.Compile())
You would have to use your Expression on the ProductItemVendorsContext like this:
var item = Context.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds);
You cant use an Expression inside query syntax, you need to use method sytanx
var results = from v in Repository.Query<ProductItemVendor>()
.Where(CompareProductItemVendorIds)
select v;
The first case;
ProductItemVendor productItemVendor = ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds);
Produces the error:
`Instance argument: cannot convert from 'System.Collections.Generic.ICollection' to 'System.Linq.IQueryable'
Happens because it is an ICollection, the entity has already been loaded. Given dbContext or objectContext lazy loading vs explicit loading is implemented a bit differently, I assume you are using explicit loading though. If you change the loading to lazy the type of ProductItemVendors will be IQueryable and what you are trying will succeed.
Given the second case, the expression must be compilable to SQL, else you get a lot of weird errors, probably it's possible that that is the case here.
It's hard to give you more explicit help given the information in the question, I cannot recreate it easily. If you can create a MWE-solution and upload it somewhere I can have a look, but I'm afraid I can't help more here.
I don't think composing expressions like that is supported by default.
You could try LINQKit
Here's an example from the LINQKit page; one Expression<Func<>> inside another:
Call Invoke to call the inner expression Call Expand on the final
result. For example:
Expression<Func<Purchase,bool>> criteria1 = p => p.Price > 1000;
Expression<Func<Purchase,bool>> criteria2 = p => criteria1.Invoke (p)
|| p.Description.Contains ("a");
Console.WriteLine (criteria2.Expand().ToString());
(Invoke and Expand are extension methods in LINQKit.) Here's the output:
p => ((p.Price > 1000) || p.Description.Contains("a"))
Notice that we have a nice clean expression: the call to Invoke has
been stripped away.

Linq lambda not working but delegate does

OK, this is doing my head in - I'm not even sure how to search for this.
Here is the first part of my function:
var rules = context.Rules.Include(r => r.CreatedBy).Include(r => r.ModifiedBy);
IUserManager um = GetUserManager();
var currentUser = um.GetCurrent();
Can someone tell me why this works:
return rules.Where(delegate(Rule r)
{
return r.CreatedBy.CompanyID == currentUser.CompanyID;
});
but this doesn't:
return rules.Where(r => r.CreatedBy.CompanyID == currentUser.CompanyID);
It's EF Code first and CreatedBy and ModifiedBy are both virtual properties.
r.CreatedBy and currentUser are both instances of the same class (if you didn't already work this out)
What the second snippet is returning is an empty list. It's as though the eager loading isn't working and the lambda doesn't cause a lazy load.
Note: I've just discovered that if I change the first line to
var rules = context.Rules.Include(r => r.CreatedBy)
.Include(r => r.ModifiedBy).ToList()
then the lambda works. The question still stands though. Why to I have to use the ToList() or the delegate. I'm doing the same thing elsewhere in the same class and it works as I'd expect.
Thanks
This is because lambdas can be implicitly converted to delegates or to expression trees. In the first case, the delegate is converted to an expression tree because rules is IQueryable<> and overload resolution chooses Queryable.Where. When you use the anonymous function, however, that can't be converted to an expression tree, so overload resolution has to choose Enumerable.Where.
When you make rules into a List<>, that forces overload resolution to choose Enumerable.Where, because List<> does not implement IQueryable<>. You could use AsEnumerable() to achieve the same effect without the overhead of creating the list.
As to why this doesn't work when you're "doing the same thing elsewhere in the same class and it works as I'd expect," we might be able to help if you give an example of code that does work.

Question about linq query format

var q = (dc.tblHelpCentreQuestions.Where(c => c.userID == UserID));
q.OrderByDescending(c => c.dateSubmitted);
I'm just getting used to Linq, and I'm sorting my records by date submitted descending, but could someone explain to me why I have to do c => c.dateSubmitted and not just pass in tblHelpCentreQuestions.dateSubmitted? What does the c=> do and why is it needed?
It is a lambda expression. Read about them here.
Also note that OrderByDescending returns a new IEnumerable, it does not do an in-place sort. You will want to read about Linq basics here.
q = tblHelpCentreQuestions is enumerable. It does not have dateSubmitted property. Its elements have that property. C stands exactly for that element
the c=>c.dateSubmitted is a lambda expression, they are used a lot with Linq. In this case, it's kind of a selector. it defines which property of your class to order by. tblHelpCentreQuestions.dateSubmitted in the other hand is just a "value", it doesn't give info about the property.
Put simply, a lambda expression is an anonymous method. a method needs parameters, that's what the c=> is for. if you have a method that takes two arguments (say, sender and args), you would have something like (sender, args)=>. There are Expression Lambdas, which have one expression as their body (as is the case with your example), and Statement Lambdas which can have more than one instruction (or statement), and thus need a block to delimit it. (sender, args)=>{ ... }. It may or may not have a return value.
Hope this helps :)

lambda expressions on populated lists

There are a few posts on the site about how to order by using lambda expressions however I cannot seem to get mine to work. I am trying to reorder a list that is already populated. Am i wrong in thinking that i can rearrange the order of this list using lambada expressions?
QuarterMileTimes.OrderByDescending(c => c.PquartermileTime);
I was wondering if it's down to PquartermileTime being a string? I also tried this expression on a date
QuarterMileTimes.orderBy(c => c.RaceDay);
Still no luck where am I going wrong?
When you call OrderByDescending, the method returns a new IEnumerable<T> - it does not reorder the collection in place.
Try doing:
QuarterMileTimes = QuarterMileTimes.OrderByDescending(c => c.PquartermileTime).ToList();
(This is if your collection is a List<T>...)
The result of OrderByDescending (and all of the other Enumerable extension methods) is an IEnumerable<T> that projects the source data in the order you're describing. It does not alter the original data in any way.
If you prefer, you can use the ToList() extension method to create a new List<T> from that result and assign it back to the original variable.
QuarterMileTimes = QuarterMileTimes.OrderByDescending(/*...*/).ToList();
(This is assuming, of course, that QuarterMileTimes is a List<T>)
The gist of the answer is no, OrderByDescending does not alter the data source in any way.
You are assigning it to a new variable aren't you?
var sortedTimes = QuarterMileTimes.OrderByDescending(c => c.PquartermileTime);
It isn't like e.g. the List.Sort method, that sorts the existing list.
The result of the method has to be assigned to a variable.
OrderByDescending returns an IOrderedEnumerable<T> i.e. a new sequence with the items in the specified order. You'll have to re-assign QuarterMileTimes to get the behaviour you expect:
QuarterMileTimes = QuarterMileTimes.OrderByDescending(c => c.PquarterMileTime).ToList();
Alternatively you can just use the returned sequence separately, which is the usual approach.
QuarterMileTimes.OrderByDescending(c => c.PquartermileTime) returns a new enumerable, ordered by PquartermileTime. It does not reorder QuarterMileTimes in place.

Extension methods syntax vs query syntax

I'm trying to get a handle on if there's a good time to use standard linq keywords or linq extension methods with lambda expressions. They seems to do the same thing, just are written differently. Is it purely a matter of style?
var query = from p in Products
where p.Name.Contains("foo")
orderby c.Name
select p;
// or with extension methods:
var query = Products
.Where(p => p.Name.Contains("foo"))
.OrderBy(p => p.Name);
They're very similar with the second example being a bit more terse, but perhaps less expressive if you don't know what the => is doing.
Other than writing terse code, are there other advantages to using the extension methods as opposed to the LINQ syntax?
Honestly, sometimes it can be situational once you start using Funcs and Actions. Say you are using these three funcs:
Func<DataClasses.User, String> userName = user => user.UserName;
Func<DataClasses.User, Boolean> userIDOverTen = user => user.UserID < 10;
Func<DataClasses.User, Boolean> userIDUnderTen = user => user.UserID > 10;
As you can see the first one replaces the lamdba expression to get the user name, the second replaces a lamdba expression used to check if the ID is lower than 10, and let's face it, the third should be pretty easy to understand now.
NOTE: This is a silly example but it works.
var userList =
from user in userList
where userIDOverTen(user)
select userName;
Versus
var otherList =
userList
.Where(IDIsBelowNumber)
.Select(userName)
In this example, the second is a little less verbose since the extension method can make full use of the Func, but he Linq expression can't since it is look just for a Boolean rather than a Func that returns boolean. However, this is where it might be better to use the expression language. Say you already had a method that takes in more than just a user:
private Boolean IDIsBelowNumber(DataClasses.User user,
Int32 someNumber, Boolean doSomething)
{
return user.UserID < someNumber;
}
Note: doSomething is just there because of the where extension method being ok with a method that takes in a user and integer and returns boolean. Kind of annoying for this example.
Now if you look at the Linq query:
var completeList =
from user in userList
where IDIsBelowNumber(user, 10, true)
select userName;
You're good for it. Now the Extension Method:
var otherList =
userList
.Where(IDIsBelowNumber????)
.Select(userName)
Without a lambda expression, I really can't call that method. So now what I have to do is create a method that creates a Func based off the original method call.
private Func<DataClasses.User, Boolean> IDIsBelowNumberFunc(Int32 number)
{
return user => IDIsBelowNumber(user, number, true);
}
And then plug it in:
var otherList =
userList
.Where(IDIsBelowNumberFunc(10))
.Select(userName)
So you can see, sometimes it may just be easier to use the query approach at times.
One advantage to using LINQ extension methods (method-based queries) is that you can define custom extension methods and it will still read fine.
On the other hand, when using a LINQ query expression, the custom extension method is not in the keywords list. It will look a bit strange mixed with the other keywords.
Example
I am using a custom extension method called Into which just takes a string:
Example with query
var query = (from p in Products
where p.Name.Contains("foo")
orderby c.Name
select p).Into("MyTable");
Example with extension methods
var query = Products
.Where(p => p.Name.Contains("foo"))
.OrderBy(p => p.Name)
.Into("MyTable");
In my opinion the latter, using a method-based query, reads better when you have custom extension methods.
I think it's a good idea not to use them together and choose one and stick with it.
Mostly it's personal taste, but in the query syntax (Comprehension method) not all operators are available as was said before.
I find the Extension Methods syntax more in line with the rest of my code. I do my SQL in SQL. It's also very easy to build your expression just by adding everything on top of eachother with the extension methods.
Just my two cents.
As I cannot make comments yet I want to make one here to the answer of Programming Tool:
Why make a whole new method for the last example?? Can't you just use:
.Where(user => IDIsBelowNumber(user, 10, true))
They compile the same, and are equivalent. Personally, I prefer the lambda (extension) methods for most things, only using the statements (standard) if I'm doing LINQ to SQL or otherwise trying to emulate SQL. I find that the lambda methods flow better with code, whereas the statements are visually distracting.
I prefer the extension method syntax when I use Linq methods that have no query syntax equivalent, such as FirstOrDefault() or others like that.
I like to use the query syntax when its really a query, ie a lazy expression which evaluates on demand.
A method that looks like regular method calls (method syntax or the lambda syntax) doesn't look lazy enough, so I use that as a convention. For eg,
var query = from p in Products
where p.Name.Contains("foo")
orderby p.Name
select p;
var result = query.ToList(); //extension method syntax
If it's a not a query, I like the fluent style which looks to me consistent with other eagerly executing calls.
var nonQuery = Products.Where(p => p.Name.Contains("foo"))
.OrderBy(p => p.Name)
.ToList();
It helps me to differentiate the two styles of calls better. Of course there are situations where you will be forced to use method syntax anyway, so my convention is not very compelling enough.
One advantage of extension methods/lynda expressions is the additional operators that is offered like Skip and Take. For example, if you are creating a pagination method, being able to skip the first 10 records and take the next 10 is easy to implement.

Categories