I need a little piece of magic. I believe what I am trying to do makes sense, but if it I've not seen a problem with the plan the reasons why would be just as welcome.
I have an expression
Expression<Func<Entity, bool>>
and I want to cast/convert or even create a whole new expression:
Expression<Func<Derived, bool>>
This is being used as an EF filter query, passed as an argument to a repository method. The repository returns an enumerable of Entity, so I could use covariance easy enough, but I want to do some post processing on the query in it's derived state before returning it.
It seems to me that EF must be doing this itself internally, but I'd like to be able to run my query so that the type of the result is Derived type rather than Entity.
Thanks for helping.
Working on the Expression level, you can build a new expression having the Derived type as parameter:
var entityExpr = (Expression<Func<Entity, bool>>)(e => e.Str == "");
var derivedExpr = Expression.Lambda<Func<Derived, bool>>(entityExpr.Body, entityExpr.Parameters);
If you have your expression Expression<Func<Entity, bool>> you can add a Cast<Derived> to it to filter down to all entities that are of that specific type.
Related
We are using System.Linq.Expressions.Expression to build custom expressions which are applied on the .Where() of our IQueryable.
What I want to achieve is, to apply the .HasFlag() method (introduced in EF 6.1) on the property which is then used in the .Where() expression.
I have following code:
var memberExpression = propertyExpression as MemberExpression;
var targetType = memberExpression?.Type ?? typeof(decimal?);
var value = Enum.Parse(type, searchValue);
var hasFlagMethod = targetType.GetMethod(nameof(Enum.HasFlag));
var hasFlagExpression = Expression.Call(propertyExpression, hasFlagMethod, Expression.Convert(Expression.Constant(value), typeof(Enum)));
The value of propertyExpression is being displayed as {x.Type} and the hasFlagMethod is being shown as {Boolean HasFlag(System.Enum)} which both look fine to me.
The value of hasFlagExpression is {x.Type.HasFlag(Convert(Foo))} which also looks completely fine to me except the Convert(Foo) part but doing this was necessary otherwhise I would get another Exception that it is complaining that the parameter cannot be applied to this method as it is not System.Enum.
And at the time we enumerate the IQueryable with this .Where() we get following exception:
NotSupportedException: LINQ to Entities does not recognize the method
'Boolean HasFlag(System.Enum)' method, and this method cannot
be translated into a store expression.
Calling it directly on the IQueryable works though (we are also using EF 6.1 which added support for Enum.HasFlag()) as in
Entities.Where(x => x.Type.HasFlag(BarEnum.Foo));
But calling it like this is not an option as it needs to be generic for all our entities. (We put those .Where() conditions together according to the filtered columns in our Datatables)
There is a small not easily visible difference between the MethodInfo of the HasFlag method in your code and the one generated by the compiler. The ReflectedType property in the former case is typeof(YourEnum) while in the later - typeof(Enum). The DeclaringType property in both cases is one and the same - typeof(Enum), hence the debug display, but that's enough to break the EF query translator.
To fix the issue, simply change
var hasFlagMethod = targetType.GetMethod(nameof(Enum.HasFlag));
to
var hasFlagMethod = typeof(Enum).GetMethod(nameof(Enum.HasFlag));
I have the following code;
IQueryable<MyClass> query = listOfObjects.Where(x => x.SomeProp == 1);
I pass this to a method on a particular API that is expecting an IQueryable, which is fine.
However, I want to dynamically build up the predicate, so I'm using Expression.Lambda to achieve this, and I then .Compile it to turn it back into a Func<MyObject, bool>.
I would have expected that the following would have worked;
Func<MyClass, bool> predicate = x => GetPredicate();
IQueryable<MyClass> query = list.Fields.Where(predicate);
However, passing predicate to Where has changed the return type to IEnumerable<MyClass>, which isn't the type required by the API obviously.
I did (naively) try predicate.AsQueryable(), but the API in question (SharePoint Client Object model) just fails with a generic "Specified method is not supported." error message.
I don't know if this a limitation of the LINQ provider that is behind the scenes, but regardless... I'm keen to understand why pulling the Func out into its own variable and passing it in to Where affects the type inference in the way it does.
IQueryable is using an expression tree to build predicate. So, instead of
Func<MyClass, bool> predicate = x => GetPredicate();
use:
Expression<Func<MyClass, bool>> predicate = x => GetPredicate();
Keep in mind:
While using IQueryable expression tree is built (tree that represents operation (as operands and arguments) made on collection). In order to translate tree into other form (let's say sql query, depends on LINQ proider) translator must know all operands used in to tree. It looks like that translator in service where you are passing IQueryable don't know what does GetPredicate function do (and don't know how to translate it to sql query) so throws Not Supported Exception..
The same thing is with Func instead of Expression. Func is complied version of predicate (stored as delegate) - provider don't know how to translate delegates. When Expression is used, the predicate is stored as tree, so provider can "look inside" an expression and translate it correctly.
I'm using LINQ to Entities for Entity Framework objects in my Data Access Layer.
My goal is to filter as much as I can from the database, without applying filtering logic to in-memory results.
For that purpose Business Logic Layer passes a predicate to Data Access Layer.
I mean
Func<MyEntity, bool>
So, if I use this predicate directly, like
public IQueryable<MyEntity> GetAllMatchedEntities(Func<MyEntity, Boolean> isMatched)
{
return qry = _Context.MyEntities.Where(x => isMatched(x));
}
I'm getting the exception
[System.NotSupportedException] --- {"The LINQ expression node type
'Invoke' is not supported in LINQ to Entities."}
Solution in that question suggests to use AsExpandable() method from LINQKit library.
But again, using
public IQueryable<MyEntity> GetAllMatchedEntities(Func<MyEntity, Boolean> isMatched)
{
return qry = _Context.MyEntities.AsExpandable().Where(x => isMatched(x));
}
I'm getting the exception
Unable to cast object of type
'System.Linq.Expressions.FieldExpression' to type
'System.Linq.Expressions.LambdaExpression'
Is there way to use predicate in LINQ to Entities query for Entity Framework objects, so that it is correctly transformed it into a SQL statement.
Thank you.
You don't need LinqKit to do this. Just remember to use
Expression<Func<MyEntity, bool>>
instead of
Func<MyEntity, bool>
Something like this:
public IQueryable<MyEntity> GetAllMatchedEntities(Expression<Func<MyEntity, Boolean>> predicate)
{
return _Context.MyEntities.Where(predicate);
}
You have to use Expression because Linq to Entities needs to translate your lambda to SQL.
When you use Func your lambda is compiled to IL but when using Expression it is an expression tree that Linq to Entities can transverse and convert.
This works with expressions that Linq to Entities understands.
If it keeps failing then your expression does something that Linq to Entities can not translate to SQL. In that case I don't think LinqKit will help.
Edit:
There is no conversion needed. Just define the method GetAllMatchedEntities with an Expression parameter and use it in the same way you would with a Func parameter. The compiler does the rest.
There are three ways you can use GetAllMatchedEntities.
1) With an inline lambda expression:
this.GetAllMatchedEntities(x => x.Age > 18)
2) Define your Expression as a field (can be a variable also)
private readonly Expression<Func<MyEntity, bool>> IsMatch = x => x.Age > 18;
...then use it
this.GetAllMatchedEntities(IsMatch)
3) You can create your expression manually. The downsize is more code and you miss the compile-time checks.
public Expression<Func<MyEntity, bool>> IsMatchedExpression()
{
var parameterExpression = Expression.Parameter(typeof (MyEntity));
var propertyOrField = Expression.PropertyOrField(parameterExpression, "Age");
var binaryExpression = Expression.GreaterThan(propertyOrField, Expression.Constant(18));
return Expression.Lambda<Func<MyEntity, bool>>(binaryExpression, parameterExpression);
}
Methods used in Linq to Entities must be canonically mapped by the Linq provider in order to work. Since the Linq provider, EF in your case, was unable to map your predicate to an internal method, it threw an error.
For LINQ scenarios, queries against the Entity Framework involve
mapping certain CLR methods to methods on the underlying data source
through canonical functions. Any method calls in a LINQ to Entities
query that are not explicitly mapped to a canonical function will
result in a runtime NotSupportedException exception being thrown
Source: CLR Method to Canonical Function Mapping (http://msdn.microsoft.com/en-us/library/bb738681.aspx)
You can try to take those methods that ARE mapped and chain them into your Linq expression, or use a stored procedure. But until EF supports all of the CLR, you will be left with having to find a work-around.
On the plus side, each release seems to add a bit more to the canonical list.
Worth reading as a possible work-around: http://msdn.microsoft.com/en-us/library/dd456857.aspx
See https://stackoverflow.com/questions/41810306/appointment-scheduling....
There are two problems with your code.
The first is how you are declaring your method:
Func<T, bool> ComposeFilter
When you do that, you are returning a compiled delegate, and the Where extension method overload that gets chosen is from Enumerable instead of Queryable.
You have to declare it as:
Expression<Func<T, bool>> ComposeFilter
The second problem is that the code you wrote cannot be converted to an expression tree. Since you are not using primitives but constructing the expression dynamically, you need to learn about the stuff in System.Linq.Expressions.
I am trying to convert an Expression of type Expression<Func<Entity, bool>> to a Func<Entity, bool>.
The background here is that I am trying to mock a repository so that it will return one of a collection of mock entities for a given key value. (I could hard code the input values to the mocked method but this seems like the wrong approach for a large number of items).
So I am trying to mock the First method on my repository like this:
var collection = new List<Entity>
{
mockedEntity1,
mockedEntity2,
mockedEntity3,
...
};
mockRepository
.Setup(rep => rep.First(It.IsAny<Expression<Func<Entity, bool>>>()))
.Returns<Expression<Func<Entity, bool>>>(e => collection.First(e));
This doesn't work because collection.First takes a Func rather than an Expression>. So I have got to the point where I need to convert the Expression to the Func that it contains.
Perhaps there a simpler or better to do this?
You need to call Compile on the expression.
It already is a lambda expression. But to get a delegate from the lambda, call .Compile().
In the general sense - to make a lambda from an Expression you would use Expression.Lambda, indicating the desired type and including the parameter (declaration) instances (from Expression.Parameter). However, this is not required here.