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 = Expression.Lambda<Func<DAL.EntityModels.Category, bool>>(body, param);
return (CategoryViewModel )_categoryRepository.GetSingle(lambda);
}
代码 _categoryRepository.GetSingle(lambda)会抛出异常:“LINQ to Entities中不支持LINQ表达式节点类型'Invoke'”
是否有简单方法来避免此异常?我不想使用其他工具,如LinqKit或PredicateBuilder。
答案 0 :(得分:6)
这进入了Linq2Entities背后的一些管道以及Linq2Objects和Linq2AnythingElse之间的区别......
你显然对表达树有很好的理解,并且你以编程方式生成它们。 Linq2Entities获取该表达式树并尝试将其转换为SQL查询以在数据库服务器上运行。但是,它无法将任意C#代码映射到其SQL等效项(例如,toBll调用在SQL中绝对没有意义)。
换句话说,你遇到了这个问题,因为Linq2Entities试图将你的toBll调用映射到SQL,并且由于没有这样的等价物而失败了。您正在尝试做的事情中存在一些设计缺陷。我假设您正试图在数据库服务器上运行“where”中表示任意条件。但是,您的任意条件是业务层对象,SQL服务器和实体框架都不了解这些对象。
对于这种设计,您真正需要做的是使用Linq2Entities类型表示任意条件,而不是BLL类型。由于Linq2Entities知道这些类型,它将能够将任意表达式转换为SQL(因为它具有Linq2Entities类型与其SQL等价物的映射)。
我上面描述的实际上是正确的方法,或者,您可以枚举查询(将执行),然后针对返回的结果集运行条件。从那时起,你在Linq2Objects(它只是针对内存中对象运行的标准.NET代码)中运行,你的函数将运行没有问题。但是,这意味着你的“where”子句将在内存中运行,而不是在数据库服务器上运行,所以我真的不推荐这个
编辑:OP请求代码......
为了使其正常工作,您需要更改GetSingle方法以获取作用于EntityFramework类型的表达式条件,而不是BLL类型。然后,您可以从表达式树中删除转换器子句,并且应该启动并运行:
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));
}
这种方法的问题在于,您的表达式必须与您的EntityFramework类型有关,这可能违反您隐藏数据抽象层细节的愿望。那时候,它真的很难运气,EntityFramework + BLL +动态查询生成=很难做对