I have this function:
public List<T> Find(Expression<Func<T, bool>> query)
{
}
Find(x => x.Id == 4);
Inside the method Find I want to chain And Condition.
something like:
query.And(x => x.Secured == false);//Secured is a memeber inside T like Id.
Your problem is that you want to access a member of T within the generic method. T could be anything at this point so the compiler will not let you access Secured since T may not have a Secured member.
You could cast T to dynamic, but this just changes a compile time error to a runtime error (plus it's horrible).
The best way would be to ensure T implements some known interface that has a Secured member.
public List<T> Find(Expression<Func<T, bool>> query) where T : ISecured
The expression must be "opened" and rebuilt, like this:
public List<T> Find<T>(Expression<Func<T, bool>> query)
{
ParameterExpression parameter = query.Parameters[0];
Expression body = query.Body;
MemberExpression property = Expression.Property(parameter, "Secured");
body = Expression.AndAlso(body, Expression.Not(property));
Expression<Func<T, bool>> query2 = Expression.Lambda<Func<T, bool>>(body, parameter);
// Now you can use query2
return null;
}
Note that I'm considering this x.Secured == false to be equivalent to !x.Secured. Clearly Secured could be a strange class that overloads the == operator, but I'll ignore that case.
As suggested by #Ralf, you could even simply do two .Where. like:
public List<T> Find<T>(Expression<Func<T, bool>> query)
{
ParameterExpression parameter = query.Parameters[0];
MemberExpression property = Expression.Property(parameter, "Secured");
Expression<Func<T, bool>> query2 = Expression.Lambda<Func<T, bool>>(Expression.Not(property), parameter);
return context.Set<T>
.Where(query)
.Where(query2)
.ToList();
}
(I'm using as an example context.Set<T>, that is very similar to what you would do if you are using Entity Framework, but in general nearly all the IQuerable<>/IEnumerable<> treat two .Where() like a single .Where() with an && condition)
Something like
Find(x => x.Id == 4 && x.Secured == false);
Related
I was using EF.Functions.Like method for my query on my ASP.NET Core 3.1 SPI but as soon as I upgraded it to .NET 6 along with with all nugets, this particular query started to throw following error
InvalidOperationException : The Like method is not supported because the query has switched to client-evaluation
I have done some research and I know InMemory databases can cause this issue, but thats not the case I've tried localhosting the API and also published SPI and in both cases the database is always on SQL Server (not local on my machine) so its not an inmemory database, also the fact it worked fine on asp.net core 3.1
Example query
Query Method
public static async Task<IQueryable<Evidence>> QueryEvidencesAsync(this EvidenceContext Context, EvidenceQuery query)
{
var predicate = PredicateBuilder.True<Evidence>();
predicate = predicate.And(x => x.IsActive && x.EvidenceStatus != EvidenceStatus.ReleasedToOwner && x.EvidenceStatus != EvidenceStatus.Transfer);
if (query.EvidenceStatus is not EvidenceStatus.Disposed)
{
predicate = predicate.And(x => x.IsActive && x.EvidenceStatus != EvidenceStatus.Disposed);
}
if (query.EvidenceStatus is not EvidenceStatus.Incoming)
{
predicate = predicate.And(x => x.IsActive && x.EvidenceStatus != EvidenceStatus.Incoming);
}
var predicateAndOr = query.IsOr ? PredicateBuilder.False<Evidence>() : PredicateBuilder.True<Evidence>();
if (!string.IsNullOrWhiteSpace(query.CaseNumber))
{
predicateAndOr = predicateAndOr.AndOr(x => EF.Functions.Like(x.CaseFileNumber, $"%{query.CaseNumber}%"), query.IsOr);
}
predicate = predicate.And(predicateAndOr);
var predicateCompiled = predicate.Compile();
return query.IncludeRequests
? Context.Evidences.Include(x => x.Requests).AsNoTracking().Where(predicateCompiled).AsQueryable().OrderByDescending(x => x.Modified)
: Context.Evidences.Where(predicateCompiled).AsQueryable().OrderByDescending(x => x.Modified);
}
PredicateBuilder
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>()
{
return (T _) => true;
}
public static Expression<Func<T, bool>> False<T>()
{
return (T _) => false;
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
InvocationExpression right = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, right), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
InvocationExpression right = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, right), expr1.Parameters);
}
public static Expression<Func<T, bool>> AndOr<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2, bool or = false)
{
if (!or)
{
return expr1.And(expr2);
}
return expr1.Or(expr2);
}
}
in this particular example, query.IsOr will always be false so predicate will always build with And predicate. Also query.CaseNumber will have the string value for caseNumber
I have removed many more conditions and if statement all of them just checked for one property and add that check to predicate, but as I said they are not relevant here as in this scenario only the if statements I've shown here are true and hence validated.
You're building a predicate as an Expression<Func<Evidence, bool>>, but then you're .Compile()ing it into a Func<Evidence, bool>. That means when you call .Where() you're no longer calling Queryable.Where<T>(this IQueryable<T> source, Expression<Func<Evidence, bool>> criteria), but rather Enumerable.Where(this IEnumerable<T> source, Func<Evidence, bool> criteria).
The difference there is that the method you're calling will treat the query as an IEnumerable<T>, and rather than allowing Entity Framework to issue a query against the database with your criteria as a WHERE clause, it will bring all of the objects back from the database, and then pass them into the criteria function (which has been compiled into memory) to determine which ones to keep.
In other words, your code in the past was terribly inefficient, and you didn't realize it. EF.Functions.Like was never meant to be invoked: it was only supposed to be evaluated as part of a query. Earlier versions of EF.Functions.Like did their best to mimic the behavior of SQL's LIKE when they were called, whereas newer versions will just throw an exception to warn you that you're doing it wrong.
You need to avoid .Compile(), and pass predicate directly into your .Where() call.
predicate = predicate.And(predicateAndOr);
return query.IncludeRequests
? Context.Evidences.Include(x => x.Requests).AsNoTracking().Where(predicate).OrderByDescending(x => x.Modified)
: Context.Evidences.Where(predicate).OrderByDescending(x => x.Modified);
That may lead to other issues, but it'll get you one step closer to doing it right.
I'm writing a custom Entity Framework filter. I have a list of IDs, and a user-supplied expression.
protected IEnumerable<TColumn> FilterIds;
protected Expression<Func<T, IEnumerable<TColumn>, bool>> Filter;
Now my question is how do I apply the Filter expression to a Where() clause?
public virtual IQueryable<T> ApplyFilter(IQueryable<T> query)
{
if (FilterMode == FilterModeMatchAny && HasFilterIds)
{
// Whoops! Can't do this!
return query.Where(x => Filter(x, FilterIds));
}
return query;
Using the Combine method from this post does pretty much everything you need. From there you just need to turn the literal value into an expression that computes it (or I guess alter the Combine method from that answer so that the intermediate value isn't computed from a lambda but rather is just any expression), and then call the function.
protected IEnumerable<TColumn> FilterIds;
protected Expression<Func<T, IEnumerable<TColumn>> FilterIdsExpression => _ => FilterIds;
protected Expression<Func<T, IEnumerable<TColumn>, bool>> Filter;
public virtual IQueryable<T> ApplyFilter(IQueryable<T> query)
{
if (FilterMode == FilterModeMatchAny && HasFilterIds)
{
return query.Where(FilterIdsExpression.Combine(Filter));
}
return query;
}
You can create a new expression to invoke the Filter using the FilterIds and use Expand from LinqKit
Edit Thanks to #Servy's comment. Now I expand only the internal expression instead of entire query.
...
protected IEnumerable<TColumn> FilterIds;
protected Expression<Func<T, IEnumerable<TColumn>, bool>> Filter;
public virtual IQueryable<T> ApplyFilter(IQueryable<T> query)
{
if (FilterMode == FilterModeMatchAny && HasFilterIds)
{
Expression<Func<T, bool>> expression = x => Filter.Invoke(x, FilterIds);
return query.Where(expression.Expand());
}
return query;
}
...
I am constructing an IQueryable query using several methods. The methods are somehow complex, but the problem I want to solve can be extracted and simplified as follows. I have two methods
private Expression<Func<T, bool>> CreateExpressionA(string ValueA)
{
return a => a.PropertyA.ToLower() == ValueA;
}
private Expression<Func<T, bool>> CreateExpressionB(string ValueB)
{
return a => a.PropertyB.ToLower() == ValueB;
}
and what I would rather have is this:
private Expression<Func<T, bool>> CreateExpression(??? Selector, string Value)
{
return a => a.Selector.ToLower() == Value;
}
or a similar approach that would allow me to avoid having two same methods with the only difference being in what property of an object is being used there.
Is it possible to do this in some elegant way?
You can pass in a selector Func that returns a string property:
private Expression<Func<T, bool>> CreateExpression<T>(Func<T, string> selector, string value)
{
return a => selector(a).ToLower() == value;
}
Usage:
CreateExpression<MyType>(x => x.PropertyA, "thevalue");
You could use reflection, more precisely the class PropertyInfo as an argument, but the implementation would be more involved. The method could be implemented as follows.
private Expression<Func<T, bool>> CreateExpression(PropertyInfo iInfo, string Value)
{
return a => (iInfo.GetPropertyValue(a) as string).ToLower() == ValueB;
}
However, note that this will work only if the type of the property is string, otherwise an additional type parameter could be used.
What you can do is create the Expression from scratch:
private Expression<Func<T, bool>> CreateExpression(string propertyName, string value) {
ParameterExpression param = Expression.Parameter(typeof(T));
MemberExpression property = Expression.Property(param, propertyName);
var valExpr = Expression.Constant(value);
var body = Expression.Equal(property, valExpr);
return Expression.Lambda<Func<T, bool>>(body, param);
}
Call with:
var expression = CreateExpression<TypeOfA>("PropertyA", "ValueForPropertyA");
It's a bit off the top of my head, but I think this should at least get you started. Let me know if you need help.
I am creating an IQueryable that I want to use for a query passed to entity framework. My repository does not expose queryable.
var query = new List<Entity>().AsQueryable().Where(x => x.Property == "argument");
I have a method on my repository that will take in an IQueryable.
How do I query my DbSet with the same queryable? I am trying to extract the expression from the queryable to build a new expression for the dbset. Here is what I have so far but it does not work:
public IDbSet<TEntity> DbSet { get; set; }
public IEnumerable<TEntity> Find(IQueryable<TEntity> queryable)
{
var parameter = Expression.Parameter(typeof (TEntity));
var body = queryable.Expression;
var lambda = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
var result = DbSet.Where(lambda);
return null;
}
The code fails when I try and create the lambda with the following error:
Expression of type 'System.Linq.IQueryable`1[MyTEntity]' cannot be used for return type 'System.Boolean'
I'm clearly not building the expression correctly, what am I missing? Is there an easier way to do what I'm trying to accomplish?
Also I've seen some examples that show an Expression should have a parameters property. But no matter what type of expression type I cast to, and this one is ConstantExpression, I don't see a parameters property from the IQueryable.Expression.
In your case, queryable.Expression represents the whole expression Queryable.Where(constantList, x => x.Property == "argument"). If you want just the Where() lambda, you need to extract it. To do that, you could use code like this:
public IEnumerable<TEntity> Find<TEntity>(IQueryable<TEntity> queryable)
{
var methodCall = queryable.Expression as MethodCallExpression;
Func<IQueryable<TEntity>, Expression<Func<TEntity,Boolean>>, IQueryable<TEntity>> whereDelegate = Queryable.Where;
if (methodCall.Method == whereDelegate.Method
&& methodCall.Arguments[0] is ConstantExpression)
{
var whereLambdaQuote = (UnaryExpression)methodCall.Arguments[1];
var whereLambda = (Expression<Func<TEntity, bool>>)whereLambdaQuote.Operand;
var result = DbSet.Where(whereLambda);
}
return null;
}
I have an expression like this
(a,b) => a.Id == b.Id
I would like to use it in LINQ to Entities query
T GetSingle(IRepository<T> repository, Func<T,T,bool> predicate, T entity)
{
return repository.GetAll().Single(e => predicate(e, entity))
}
but this results the exception: LINQ expression node type 'Invoke' is not supported in LINQ to Entities
As I understand I can use Expressions to construct a valide predicate for LINQ2SQL, so my expression
(a,b) => a.Id == b.Id and instance of entity with Id = 5 can result a new expression (a) => a.Id == 5.
And the last expression will be fine for LINQ to Entities.
I found and read this articles
Replace parameter in lambda expression
http://www.codeproject.com/Articles/143096/Parameter-Substitution-within-Expression-Trees
but still has no clue how to solve my task
So, how do I convert given expression dynamically?
Why don't you just change your method to be:
T GetSingle(IRepository<T> repository, Expression<Func<TSource, Boolean>> predicate)
{
return repository.GetAll().Single(predicate);
}
so instead of this:
GetSingle(myRepository, (a,b) => a.Id == b.Id, myEntity);
you should be able to do this:
GetSingle(myRepository, a => a.Id == myEntity.Id);
I haven't tested it with Linq2SQL, but it seems to me that you should be able to do this with an expression visitor and compiling the expression to write the value of your parameter into the expression you've supplied (assuming you switch over to using Expression<Func<T, T, bool>> instead of Func<T, T, bool>) and creating a wrapper that itself invokes Enumerable.Single on the result from the GetAll
The visitor (for specifically the example you've given would look like this)
public class VariableSubstitutionVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly ConstantExpression _constant;
public VariableSubstitutionVisitor(ParameterExpression parameter, ConstantExpression constant)
{
_parameter = parameter;
_constant = constant;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _parameter)
{
return _constant;
}
return node;
}
}
Now, we'd adjust the GetSingle method to look like this:
public T GetSingle(IRepository<T> repository, Expression<Func<T, T, bool>> predicate, T entity)
{
//Create a new representation of predicate that will take just one parameter and capture entity
//Get just the body of the supplied expression
var body = predicate.Body;
//Make a new visitor to replace the second parameter with the supplied value
var substitutionVisitor = new VariableSubstitutionVisitor(predicate.Parameters[1], Expression.Constant(entity, typeof(T)));
//Create an expression that represents the predicate with the second parameter replaced with the supplied entity
var visitedBody = substitutionVisitor.Visit(body).Reduce();
//Make the new expression into something that could be a Func<T, bool>
var newBody = Expression.Lambda<Func<T, bool>>(visitedBody, predicate.Parameters[0]);
//Now, create something that will call Enumerable.Single on the result of GetAll from the repository, supplying the new predicate
//Make a place to hold the result of GetAll
var resultsParameter = Expression.Parameter(typeof (IEnumerable<T>));
//Make an expression that calls the Single extension method
var singleExpression = Expression.Call(((Func<IEnumerable<T>, Func<T, bool>, T>)Enumerable.Single).Method, resultsParameter, newBody);
//Make a Func<IEnumerable<T>, T> that return the result of the call of Single on the results of the GetAll method
var compiled = Expression.Lambda<Func<IEnumerable<T>, T>>(singleExpression, resultsParameter).Compile();
//Call GetAll, letting the new method that we've got run the supplied predicate without having to run an Invoke type expression
return compiled(repository.GetAll());
}
The trick, of course, is getting that to perform well.