The LINQ expression node type 'Invoke' is not supported in LINQ to Entities
public CategoryViewModel GetSingle( Expression<Func<CategoryViewModel, bool>> where)
{
Expression<Func<DAL.EntityModels.Category, CategoryViewModel>> converter =
c => ToBll(c);
var param = Expression.Parameter(typeof(DAL.EntityModels.Category), "category");
var body = Expression.Invoke(where, Expression.Invoke(converter, param));
var lambda = Expressi开发者_C百科on.Lambda<Func<DAL.EntityModels.Category, bool>>(body, param);
return (CategoryViewModel )_categoryRepository.GetSingle(lambda);
}
The code _categoryRepository.GetSingle(lambda) throws an exception: "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities"
Is there easy way to avoid this exception? I don't want to use another tools like LinqKit or PredicateBuilder.
This gets into some of the plumbing behind Linq2Entities and the difference between Linq2Objects and Linq2AnythingElse...
You obviously have a good understanding of expression trees and you're generating them programatically. Linq2Entities takes that expression tree and tries to convert it to a SQL query to run on the database server. However, It can't map arbitrary C# code onto its SQL equivalent (for example, the toBll call has absolutely no significance in SQL).
In other words, you're hitting this problem because Linq2Entities is attempting to map your toBll call into SQL, and is failing miserably because there is no such equivalent. There is a bit of a design flaw in what you're trying to do. I'm assuming that you're trying to get the arbitrary condition expressed in "where" to run on the database server. However, your arbitrary condition is in terms of your business layer objects, and neither SQL server nor the entity framework know anything about these objects.
What you really need to do for this sort of design, is to have the arbitrary condition expressed in terms of the Linq2Entities types, not your BLL types. Since Linq2Entities knows about these types, it will be able to translate the arbitrary expression into SQL (since it has the mappings for the Linq2Entities types to their SQL equivalents).
What I described above is really the proper way to do this, alternatively, you could enumerate the query (which will execute) and then run the conditions against the returned result set. Since at this point you're running in Linq2Objects (which is just standard .NET code running against in-memory objects), your functions will run without issues. However, this means that your "where" clause will be run in-memory, and NOT on the database server, so I would really not recommend this
EDIT: OP requested code...
In order for this to work properly, you need to change your GetSingle method to take an expression condition that acts on the EntityFramework type, and not on your BLL type. You can then remove your converter clause from the expression tree, and you should be up and running:
public CategoryViewModel GetSingle( Expression<Func<DAL.EntityModels.Category, bool>> where)
{
var param = Expression.Parameter(typeof(DAL.EntityModels.Category), "category");
var body = Expression.Invoke(where, param);
var lambda = Expression.Lambda<Func<DAL.EntityModels.Category, bool>>(body, param);
return ToBLL((DAL.EntityModels.Category)_categoryRepository.GetSingle(lambda));
}
The problem with this approach is that your expression must be in terms of your EntityFramework type, which might violate your desire to hide the details of the data abstract layer. At that point, its pretty much tough luck, EntityFramework + BLL + Dynamic Query Generation = hard to get right
精彩评论