我最近在how to write customized expression tree上发了一个问题(虽然我没有意识到那是我当时要求的)。现在我试图使用Scott Chamberlain提供的答案来创建另一种通用方法。
我正在使用Entity Framework并且有几个可以通过“Name”属性过滤的实体。它并不总是名为Name,但基本上就是它。
实际的过滤方法比简单的包含或开始更复杂。它更像是一个Starts With,然后将包含搜索文本的名称的任何记录追加到列表的末尾。
由于有太多实体需要执行此过滤器,我希望能够创建一个将该过滤器应用于预编译查询的扩展方法。
我希望能够致电
someQuery.ContainsName(x => x.Name, filter.Name);
我有一个看起来像这样的扩展方法
public static IQueryable<T> ContainsName<T>(this IQueryable<T> query, Expression<Func<T, string>> selector, string matchingName)
{
var name = Expression.Constant(matchingName);
var selectorBody = selector.Body;
var propertyName = (selectorBody as MemberExpression).Member.Name;
var propExpression = Expression.Parameter(typeof(string), propertyName);
var selectorParameters = selector.Parameters;
Expression check = Expression.Call(typeof(GenericNameFilter).GetMethod("IsMatch"), propExpression, name);
var lambada = Expression.Lambda<Func<T, bool>>(check, selectorParameters);
return query.Where(lambada)
}
IsMatch只接受2个字符串并返回一个bool。这不是该方法的最终游戏,但我希望能够让IsMatch的简单版本正常工作,然后我会扩展它。
当我在结果上调用ToList时,我得到一个异常。
“LINQ to Entities无法识别方法'Boolean IsMatch(System.String,System.String)'方法,并且此方法无法转换为商店表达式。”
从我所做的搜索中,我相信我想要做的事情是可能的,我只是做错了。这是我设置表达式树的方式吗?是IsMatch方法吗?一切都是错的吗?
答案 0 :(得分:5)
问题的关键在于你不能编写导致代码运行的查询操作,因为数据库显然无法在其数据上运行C#代码。
您的扩展方法会向查询管道添加lambda调用;你打算将它解决为IsMatch
方法的一些重载。但是,这对数据库没有任何意义,因此EF查询提供程序会告诉您它无法转换您写入SQL的内容。不幸的是,没有办法解决这个问题。
相同的代码在LINQ to对象上运行得很好,因为在那种情况下编译器当然可以安排调用你的方法。