Cross reference collections in Expression<Func<>> - c#

I have the following expression that I am using in a linq to entities query
private Expression<Func<PageElement, bool>> ViewerProfileCheckExp(IViewerProfileModel vpSource)
{
return (pe) => pe.ViewerProfiles.Any(vp => vp.ViewLevel.Id == vpSource.ViewLevelId &&
vp.ViewTransitId == vpSource.ViewTransitId &&
vp.ViewGroups.ContainsAny(vpSource.Groups));
}
In the last clause I would like to be able to return true in the condition if any of the ViewGroups in vp are contained in vpSource.Groups. I realize that ContainsAny does not exist but I am wondering how to integrate what I want into the expression.

What you're logically looking for is whether or not the intersection of the two collections has any items:
vp.ViewGroups.Intersect(vpSource.Groups).Any()

Related

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.

Using an expression with a linq query

At the moment I have a linq query with a method residing inside of it. I'm getting the error LINQ to Entities does not recognize the method. So I found I can convert the method to be an expression LINQ to Entities does not recognize the method but I'm wondering is there a clean and easy way to add the expression to my linq query.
Original Method
public bool IsAvailable()
{
return Eligibility.ProgramType == InteractionProgramTypes.Available;
}
Changed to
public System.Linq.Expressions.Expression<Func<InteractionProgram, bool>> IsAvailable()
{
return i => i.Eligibility.ProgramType == InteractionProgramTypes.Available;
}
Linq query without expression
x => x.ActivityDate <= endDate && x.IsAvailable()
Linq query with expression
x => x.ActivityDate <= endDate && x.IsAvailable().Compile()
When doing that I get the compiler error, && operator cannot be applied to operands.
May I ask how do I append the expression to my current linq query.
Since you already converted IsAvailable to return Expression<Func<InteractionProgram,bool>>, all you need to do is to pass the result of calling this method to the Where method of IQueryable<T>:
var res = ctx.InteractionPrograms.Where(InteractionProgram.IsAvailable());
Note that in order for this to compile your IsAvailable method needs to be static. Moreover, you could make it a property for an even better readability:
class InteractionProgram {
public static Expression<Func<InteractionProgram,bool>> IsAvailable {get;} =
i => i.Eligibility.ProgramType == InteractionProgramTypes.Available;
... // other members of the class
}
...
var res = ctx.InteractionPrograms.Where(InteractionProgram.IsAvailable);
what about the other condition x.ActivityDate <= endDate. Where would that go?
Other conditions go into separate Where clauses either immediately before or immediately after IsAvailable condition. EF driver will combine the two expressions for you, resulting in a single query on RDBMS side.
Another alternative to sharing this expression would be creating an extension method on the EF context that returns IQueryable<InteractionProgram> pre-filtered for availability:
public static IQueryable<InteractionProgram> AvailableInteractionPrograms(this MyDbContext dbCtx) =>
dbXtx.InteractionPrograms.Where(i =>
i.Eligibility.ProgramType == InteractionProgramTypes.Available
);
This hides the function behind a shared method.

Using multiple clauses in where

What is the correct way to include multiple wheres in a LINQ call for OR
List<Pos> posList = DbContext.PosList
.Where<Pos>(p => p.Pos == "51000785" ||
p => p.Pos == "123")
.ToList<Pos>();
The Linq where clause takes one expression and returns one bool value. Yours is taking two expressions each with their own return value. You would need to combine these two into one lambda expression that returns one value rather than the two separate ones in your example.
List<Pos> posList = DbContext.PosList
.Where<Pos>(p => p.Pos == "51000785" || p.Pos == "123")
.ToList<Pos>();

C# NET LINQ to Entities does not recognize the method

I'm getting this error when trying to do a linq query:
LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable`1[FEI.Entities.EF6.Tables.HORSE_IDENTITY_GENDER] GetHorseIdentityGenderQuery(FEI.Entities.EF6.Tables.HORSE_DOCUMENT_PART)' method, and this
method cannot be translated into a store expression.
I've read a lots of previous questions where people get the same error, but I understand that it's because LINQ to Entities requires the whole linq query expression to be translated to a server query, and therefore you can't call an outside method in it. I haven't been able to convert my scenario into something that works yet, and my brain is starting to melt down, so I was hoping someone could point me in the right direction. We're using Entity Framework and the specification pattern (and I'm new to both).
Here's the code that uses the specification:
HORSE_DOCUMENT HorseDocForPart = Bll.GetHorseDocumentForPartUpload(horseId, identityType);
Here's the code that provides from method GetHorseDocumentForPartUpload
public HORSE_DOCUMENT GetHorseDocumentForPartUpload(int horseID, HorseDocGender identityType)
{
// Get the unique horse document
var horseDoc = Tables
.HORSE_DOCUMENT
.Where(hd =>
hd.HORSE_UID == horseID &&
hd.HORSE_IDENTITY_TYPE.HORSE_IDENTITY_TYPE_FULL_CODE == identityType.ToString() &&
!hd
.HORSE_DOCUMENT_PART
.Any(hdp =>
hdp.VALIDATION_STATUS != HorseDocPartStatus.REFUSED.ToString() &&
GetHorseIdentityGenderQuery(hdp).Any(hig => hig.IS_FULL)
)
).SingleOrDefault();
return horseDoc;
}
Here's the last code :
public IEnumerable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(HORSE_DOCUMENT_PART horseDocPart)
{
var possibleDocs = Tables
.DOCUMENTs
.Where(doc => doc.DOC_OWNER_UID == horseDocPart.HORSE_DOCUMENT_PART_UID);
return horseDocPart
.HORSE_DOCUMENT
.HORSE_IDENTITY_TYPE
.HORSE_IDENTITY_GENDER
.Join(
possibleDocs,
hig => hig.DOCUMENT_GENDER_CODE.DOCUMENT_GENDER_CODE_UID,
doc => doc.DOCUMENT_GENDER_CODE_UID,
(dgc, doc) => dgc
);
}
You return IEnumerable from the method
public IEnumerable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(...)
This is deferred but using IEnumerable does not allow Linq-To-Sql execution, you should be using IQueryable as such.
public IQueryable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(...)
Please see more detailed explanation from Returning IEnumerable<T> vs. IQueryable<T>

How to use System.Linq.Expressions.Expression to filter based on children?

I have a filter that I use across many methods:
Expression<Func<Child, bool>> filter = child => child.Status == 1;
(actually is more complex than that)
And I have to do the following
return db.Parents.Where(parent => parent.Status == 1 &&
parent.Child.Status == 1);
where the condition is the same as in the filter above.
I want to reuse the filter in this method. But I don't know how. I tried
return db.Parents.Where(parent => parent.Status == 1 &&
filter(parent.Child));
but an Expression can't be used as a method
If you want to combine expressions and still be able to use linq-to-sql, you may want to have a look at LinqKit. It walks inside your expression and replaces all the function calls by their contents before the sql conversion.
This way you'll be able to use directly
return db.Parents
.AsExpandable()
.Where(parent => parent.Status == 1 && filter(parent.Child));
You can try this:
var compiledFilter = filter.Compile();
foreach (var parent in db.Parents.Where(parent => parent.Status == 1))
if (compiledFilter(parent.Child))
yield return parent;
It requires you to pull all of the parents, but unlike #HugoRune's solution, it doesn't require a 1:1 relation of Parent:Child.
I don't think this will be useful for your situation because of the different types involved, but just in case, here is an example of how you can combine Expressions: How do I combine LINQ expressions into one?
Edit: I had previously suggested using Compile(), but that doesn't work over LINQ-to-SQL.
Well, if there is a 1:1 relationship between parent and child
(unlikely, but the example seems to imply that) then you could do it like this:
return db.Parents
.Where(parent => parent.Status == 1)
.Select(parent => parent.Child)
.Where(filter)
.Select(child=> child.Parent);
Otherwise it will be hard.
You could do it with dynamic linq but that is probably overkill.
You could generate your expression tree manually, but that is also quite complicated. I have not tried that myself.
As a last resort you could of course always call yourQuery.AsEnumerable(), this will cause linq-to-sql to translate your query into sql up to this point and perform the rest of the work on the client-side; then you can .compile() your expression. However you lose the performance benefits of linq-to-sql (and compile() itself is quite slow; whenever it is executed, it calls the JIT-compiler):
return db.Parents
.Where(parent => parent.Status == 1)
.AsEnumerable()
.Where(parent => filter.Compile().Invoke(parent.Child))
Personally I'd just define the expression twice, once for child and once for parent.child:
Expression<Func<Child, bool>> filterChild = child => child.Status == 1;
Expression<Func<Parent, bool>> filterParent = parent => parent.Child.Status == 1;
Might not be the most elegant, but probably easier to maintain than the other solutions
Just come up with this, check if this would work for you
public interface IStatus { public int Status { get; set; } }
public class Child : IStatus { }
public class Parent : IStatus
{public Child Child { get; set; } }
Func<IStatus, bool> filter = (x) => x.Status == 1;
var list = Parents.Where(parent => filter(parent) && filter(parent.Child));
Hope this helps!
Could you just use the expression as a function instead?
Instead of:
Expression<Func<Child, bool>> filter = child => child.Status == 1;
Use that same expression as a generic function this way:
Func<Child, bool> filter = child => child.Status == 1;
Then you will be able to use the function in just the same way you were trying to use an expression:
return db.Parents.Where(parent => parent.Status == 1 &&
filter(parent.Child));
Edit: I misunderstood the question. This is a bad answer. 6+ years out, I'm still getting comments to the effect that this doesn't work. I'm not sure, from a hygiene perspective, if it would be better to just delete the answer, or add this edit and let the answer stand as an example of something that decidedly doesn't work. I'm open to advisement on that.
There's no need for external libraries or mucking around with expression trees. Instead, write your lambda functions to use query chaining and take advantage of LINQ's deferred execution.
Instead of:
Expression<Func<Child, bool>> filter = child => child.Status == 1;
Rewrite it as:
Func<IQueryable<Parent>, IQueryable<Parent>> applyFilterOnParent = query => query.Where(parent => parent.Child.Status == 1);
Func<IQueryable<Child>, IQueryable<Child>> applyFilterOnChild = query => query.Where(child => child.Status == 1);
Now, instead of:
return db.Parents.Where(parent => parent.Status == 1 &&
filter(parent.Child));
You can write:
var query = db.Parents.AsQueryable();
query = applyFilterOnParent(query);
return query.Where(parent => parent.Status == 1);
And you can re-use the applyFilter functions in other LINQ queries. This technique works well when you want to use lambda functions together with LINQ-to-SQL, because LINQ will not translate a lambda function to SQL.

Categories