Can I use an embedded lambda with the Contains method? - c#

I want to filter a list with FindAll
If I write:
.FindAll(
p => p.Field == Value &&
p.otherObjList.Contains(otherObj));
it's ok, but if I write
.FindAll(
p => p.Field == Value &&
p.otherObjList.Contains(
q => q.Field1 == Value1 &&
q.Field2 == Value2));
I get C# syntax error message: Unknown Method FindAll(?) of .. the otherObjList
I cannot define the otherObj exactly, because I know only the values of two fields, Field1 and Field2.
What have I done wrong? What can I do in this case?

The Contains() method for both most collection types as well as the LINQ version expects an argument of the same type as the collection, not a lambda.
It appears you are just trying to check if any item matches some condition. You should use the Any() method.
.FindAll(p => p.Field == Value
&& p.otherObjList.Any(q => q.Field1 == Value1 && q.Field2 == Value2))

Related

How to compare and select particular object from list using LINQ

I've two set of list of objects. One named as SellWish and another as holdingAdviceDecisions. These two objects are linked via two items such as:
SellWishId and
HoldingAdviceId
I'm trying to get the matched values between these with the following condition
holdingAdvice.CustomerDecision == CustomerDecision.FollowsAdvice
This is what I've tried so far:
var item = sellWishes.Where(sellWish =>
holdingAdviceDecisions.Where
(holdingAdvice => sellWish.Id == holdingAdvice.TradeInstructionId
&& holdingAdvice.CustomerDecision == CustomerDecision.FollowsAdvice));
But I'm getting following errors:
Error CS1662
Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate
return type
2.Error CS0029
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<HoldingsAdvice.Contracts.HoldingAdviceDecision>' to 'bool'
Any suggestion would be highly appreciated.
you need
var items = sellWishes.Where(sellWish =>
holdingAdviceDecisions.Any
(holdingAdvice => sellWish.Id == holdingAdvice.TradeInstructionId
&& holdingAdvice.CustomerDecision == CustomerDecision.FollowsAdvice));
note that this reurns all matching entries, if you know you only the first (or null)
var item = sellWishes.Where(sellWish =>
holdingAdviceDecisions.Any
(holdingAdvice => sellWish.Id == holdingAdvice.TradeInstructionId
&& holdingAdvice.CustomerDecision == CustomerDecision.FollowsAdvice)).FirstOrDefault();

Use Expression<Func<MyEntity, bool>> in Linq on MyEntity.<OtherEntity>.<MyProperty>

I am trying to simplify a method that returns an IQueryable
A, B and C extend BaseEntity containing a enum that I want to compare.
context is a entity framework dbcontext.
Here is a stripped down version of the method:
return context.MyEntities.Include("A").Include("B").Include("C")
.Where(x => x.A.MyEnum == MyEnum.<value> && x.B.MyEnum == MyEnum.<value> && x.C.MyEnum == MyEnum.<value>);
I tried to do this:
Func<BaseEntity, bool> equals = x => x.MyEnum == MyEnum.<value>;
return context.MyEntities.Include("A").Include("B").Include("C")
.Where(x => equals(x.A) && equals(x.B) && equals(x.C));
It compiles but gives a runtime error. For what I understand is that Linq cannot translate the func<> to SQL?
So i searched and I found that you need to wrap the func<> in an expression<>, so Linq can compile it and translate it to SQL.
Now I have this:
Expression<Func<BaseEntity, bool>> equals = x => x.MyEnum == MyEnum.<value>;
return context.MyEntities.Include("A").Include("B").Include("C")
.Where(x => equals(x.A) && equals(x.B) && equals(x.C));
But this doesn't compile: 'Method name expected'.
Is there a way to accomplish what i'm trying to do?
The compile error is because you have to first compile the expression before being able to invoke it.
equals.Compile()(x.A)
But this defeats the purpose of using the expression to begin with.
There is nothing to be simplified in the provided code other than moving the repeated value call into a variable.
var value = MyEnum.<value>;
return context.MyEntities.Include("A").Include("B").Include("C")
.Where(x => x.A.MyEnum == value && x.B.MyEnum == value && x.C.MyEnum == value);
The attempt to simplify what was shown is not really needed.

Proper Syntax for Extension Method Query

I am trying to do something similar to my previous post, except I am using extension methods instead of LINQ. I get an error telling me that && cannot be used, so how would I search within a table using two strings entered by the user?
var query = (App.DBConnection.Table<Notes>().Where(
c => c.Note.Contains(textBox1.Text) && c => c.Note.Contains(textBox2.Text))).Single();
TextBox_Results.Text = query.Note;
Remove the second lambda operator c =>
var query = App.DBConnection.Table<Notes>()
.Where(c => c.Note.Contains(textBox1.Text)
&& c.Note.Contains(textBox2.Text)))
.Single();
Apart from that, i would use FirstOrDefault instead of Single. The latter throws an InvalidOperationException if there are no elements or if there are more than one. The former just returns null if no item matches the predicate in the Where.
You don't need to declare the c variable again
Where(c => c.Note.Contains(textBox1.Text) && c => c.Note.Contains(textBox2.Text)))
should be
Where(c => c.Note.Contains(textBox1.Text) && c.Note.Contains(textBox2.Text)))

Lambda vs LINQ- "Expression is always false"

I have the following code:
var thing = (from t in things
where t.Type == 1 && t.IsActive
select t).SingleOrDefault();
if (thing == null)
{
// throw exception
}
things is a collection of Entity Framework Self-Tracking Entities
This works nicely, however I want to use a Lambda expression instead and changed the LINQ to this:
var thing = things.Select(t => t.Type == 1 && t.IsActive).SingleOrDefault();
Now Resharper is telling me Expression is always false for (thing == null).
What have I missed?
You want:
var thing = things.Where(t => t.Type == 1 && t.IsActive).SingleOrDefault();
Select performs a projection (converting the type of the IEnumerable from IEnumerable<Thing> to IEnumerable<bool> with values true if t.Type == 1 && t.IsActive == true, otherwise false), then the SingleOrDefault returns either the only bool in this sequence, or the default value of a bool which is false if the sequence is empty. This can never be null since bool is not a reference type.
Where performs a filtering action (pulling out only those objects that meet a given criterion - in this case only selecting those where Type is 1 and IsActive is true), leaving the type of the IEnumerable as IEnumerable<Thing>. Assuming Thing is a class, the SingleOrDefault will return the only item in the sequence or null.
In either case, SingleOrDefault will throw an exception if the sequence contains more than one item (which is far more likely in the Select version!).

LINQ where clause with lambda expression having OR clauses and null values returning incomplete results

the problem in short
we have a lambda expression used in the Where clause, which is not returning the "expected" result.
quick summary
in the analysisObjectRepository object, there are certain objects which also contain the parent relationship in a property named Parent. we are querying this analysisObjectRepository to return some objects.
detail
what the code below supposed to do is, returning the root, the first children (immediate children) and grandchildren of a specific object containing the ID value.
in the code below, common sense says that all the results which makes any of the 3 seperate OR conditions true should be returned as in the results.
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId ||
x.Parent.ID == packageId ||
x.Parent.Parent.ID == packageId)
.ToList();
but the above code only returns the children and grandchildren, while not returning the root objects (with a null Parent value) which make the
x.ID == packageId
condition true.
only objects which make the second
x.Parent.ID == packageId
and third
x.Parent.Parent.ID == packageId
clauses are returned.
If we only write the code to return the root object with the below code, it is returned, so we are totally sure that analysisObjectRepository contains all the objects
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId )
.ToList();
However, when we rewrite it as a delegate, we get the expected result, returning all the expected objects.
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(delegate(AnalysisObject x)
{
return
(x.ID == packageId) ||
(x.Parent != null && x.Parent.ID == packageId) ||
(x.Parent != null &&
x.Parent.Parent != null &&
x.Parent.Parent.ID == packageId); })
.ToList();
question
Are we missing something in the lambda expression? it is a really simple 3 part OR condition and we think that any object that makes any of the three conditions true should be returned. we suspected that the root object having a null Parent value might cause a problem but couldn't figure it out exactly.
any help would be great.
Your second delegate is not a rewrite of the first in anonymous delegate (rather than lambda) format. Look at your conditions.
First:
x.ID == packageId || x.Parent.ID == packageId || x.Parent.Parent.ID == packageId
Second:
(x.ID == packageId) || (x.Parent != null && x.Parent.ID == packageId) ||
(x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)
The call to the lambda would throw an exception for any x where the ID doesn't match and either the parent is null or doesn't match and the grandparent is null. Copy the null checks into the lambda and it should work correctly.
Edit after Comment to Question
If your original object is not a List<T>, then we have no way of knowing what the return type of FindAll() is, and whether or not this implements the IQueryable interface. If it does, then that likely explains the discrepancy. Because lambdas can be converted at compile time into an Expression<Func<T>> but anonymous delegates cannot, then you may be using the implementation of IQueryable when using the lambda version but LINQ-to-Objects when using the anonymous delegate version.
This would also explain why your lambda is not causing a NullReferenceException. If you were to pass that lambda expression to something that implements IEnumerable<T> but not IQueryable<T>, runtime evaluation of the lambda (which is no different from other methods, anonymous or not) would throw a NullReferenceException the first time it encountered an object where ID was not equal to the target and the parent or grandparent was null.
Added 3/16/2011 8:29AM EDT
Consider the following simple example:
IQueryable<MyObject> source = ...; // some object that implements IQueryable<MyObject>
var anonymousMethod = source.Where(delegate(MyObject o) { return o.Name == "Adam"; });
var expressionLambda = source.Where(o => o.Name == "Adam");
These two methods produce entirely different results.
The first query is the simple version. The anonymous method results in a delegate that's then passed to the IEnumerable<MyObject>.Where extension method, where the entire contents of source will be checked (manually in memory using ordinary compiled code) against your delegate. In other words, if you're familiar with iterator blocks in C#, it's something like doing this:
public IEnumerable<MyObject> MyWhere(IEnumerable<MyObject> dataSource, Func<MyObject, bool> predicate)
{
foreach(MyObject item in dataSource)
{
if(predicate(item)) yield return item;
}
}
The salient point here is that you're actually performing your filtering in memory on the client side. For example, if your source were some SQL ORM, there would be no WHERE clause in the query; the entire result set would be brought back to the client and filtered there.
The second query, which uses a lambda expression, is converted to an Expression<Func<MyObject, bool>> and uses the IQueryable<MyObject>.Where() extension method. This results in an object that is also typed as IQueryable<MyObject>. All of this works by then passing the expression to the underlying provider. This is why you aren't getting a NullReferenceException. It's entirely up to the query provider how to translate the expression (which, rather than being an actual compiled function that it can just call, is a representation of the logic of the expression using objects) into something it can use.
An easy way to see the distinction (or, at least, that there is) a distinction, would be to put a call to AsEnumerable() before your call to Where in the lambda version. This will force your code to use LINQ-to-Objects (meaning it operates on IEnumerable<T> like the anonymous delegate version, not IQueryable<T> like the lambda version currently does), and you'll get the exceptions as expected.
TL;DR Version
The long and the short of it is that your lambda expression is being translated into some kind of query against your data source, whereas the anonymous method version is evaluating the entire data source in memory. Whatever is doing the translating of your lambda into a query is not representing the logic that you're expecting, which is why it isn't producing the results you're expecting.
Try writting the lambda with the same conditions as the delegate. like this:
List<AnalysisObject> analysisObjects =
analysisObjectRepository.FindAll().Where(
(x =>
(x.ID == packageId)
|| (x.Parent != null && x.Parent.ID == packageId)
|| (x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)
).ToList();
You are checking Parent properties for null in your delegate. The same should work with lambda expressions too.
List<AnalysisObject> analysisObjects = analysisObjectRepository
.FindAll()
.Where(x =>
(x.ID == packageId) ||
(x.Parent != null &&
(x.Parent.ID == packageId ||
(x.Parent.Parent != null && x.Parent.Parent.ID == packageId)))
.ToList();

Categories