使用 Func<T, string> lambda 动态构造 where 子句 - linq to entities
Dynamically construct where clause with Func<T, string> lambda - linq to entities
(这是针对 .Net Framework 4.7 的)
我正在尝试编写一些扩展方法来帮助为各种实体创建动态的 where 子句。我是几天前开始的,所以可能有很多我不知道的地方,还有一些我可能误解了。
我设法创建了一个扩展方法,用于按 1 属性 过滤,它按我预期的方式工作(我确实使用反射来获取 属性,但无法使用一个接口 - 好吧,没有它执行 sql 就是这样)。
不过,我似乎无法让这个用于 lambda 表达式。
请注意,解决方案不得触发 sql 执行。因为我能够写出一些“有效”的变体,但它们会触发 sql 执行。
我的工作方式是,一旦我准备好代码,我就开始调试并在手表中进行“查询”。它看起来像这样(注意 sql 代码)
一旦我越过我的 FilterString 方法调用,它要么变成 sql 结果,要么我得到一个异常(使用当前代码),它不应该:
所以这是我当前抛出异常的代码(目前不处理“匹配”参数,我正在实现一个“等于”调用。会有其他人喜欢,开头等等)
异常只是其中一种“类型不匹配”,其函数无法作为参数传递给字符串等式或其他类型。
public static IQueryable<T> FilterString<T>(this IQueryable<T> query, Match match,
string criteriaItem, Expression<Func<T, string>> getItemString)
where T : class
{
if (string.IsNullOrEmpty(criteriaItem))
{
return query;
}
var param = Expression.Parameter(typeof(T), "r");
var selector = Expression.Lambda<Func<T, string>>(getItemString, param);
Expression<Func<string, bool>> prototype = item => item == criteriaItem;
var predicate = Expression.Lambda<Func<T, bool>>(
prototype.Body.ReplaceParameter(prototype.Parameters[0], selector.Body),
selector.Parameters[0]);
return query.Where(predicate);
}
和执行 sql 而不是仅仅生成它的那个
public static IQueryable<T> FilterString<T>(this IQueryable<T> query, Match match,
string criteriaItem, Expression<Func<T, string>> getItemString)
where T : class
{
if (string.IsNullOrEmpty(criteriaItem))
{
return query;
}
var param = Expression.Parameter(typeof(T), "r");
//var value = Expression.Constant(getItemString);
var equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
var item = Expression.Invoke(getItemString, param);
var body = Expression.Call(Expression.Constant(criteriaItem),
equals,
item);
return query.Where(Expression.Lambda<Func<T, bool>>(body, param));
}
像这样调用这些
query = query.FilterString(match, criteria_value, (r) => r.SomeProperty.MaybeSomeOtherProp.SomeString);
query = query.FilterString(match, criteria_value, (r) => r.SomeProperty.Name);
将在任意数量的不同实体上调用相同的扩展方法,具有不同的属性和道具名称。我想我可以利用我开始工作的反射版本,并在某种数组中传递所有 属性 名称,但这简直是丑陋。
长话短说,我怎样才能按照我上面解释的方式让它工作,那就是:生成 sql 而不是执行?
谢谢,
请注意,“ReplaceParameter”扩展方法来自此处:
所以,您正在尝试合并您的原型 item => item == criteriaItem
。使用传入的字符串 属性 表达式,例如 (r) => r.SomeProperty.Name
来创建 (r) => r.SomeProperty.Name == criteriaItem
.
Expression<Func<string, bool>> prototype = item => item == criteriaItem;
var predicate = Expression.Lambda<Func<T, bool>>(
ReplacingExpressionVisitor.Replace(
prototype.Parameters[0],
getItemString.Body,
prototype.Body),
getItemString.Parameters[0]);
而且我认为您正在尝试这样做,以便 criteriaItem
绑定到 sql 参数,而不是作为字符串常量内联。但是你的问题有点难以理解。
(这是针对 .Net Framework 4.7 的)
我正在尝试编写一些扩展方法来帮助为各种实体创建动态的 where 子句。我是几天前开始的,所以可能有很多我不知道的地方,还有一些我可能误解了。
我设法创建了一个扩展方法,用于按 1 属性 过滤,它按我预期的方式工作(我确实使用反射来获取 属性,但无法使用一个接口 - 好吧,没有它执行 sql 就是这样)。 不过,我似乎无法让这个用于 lambda 表达式。
请注意,解决方案不得触发 sql 执行。因为我能够写出一些“有效”的变体,但它们会触发 sql 执行。
我的工作方式是,一旦我准备好代码,我就开始调试并在手表中进行“查询”。它看起来像这样(注意 sql 代码)
一旦我越过我的 FilterString 方法调用,它要么变成 sql 结果,要么我得到一个异常(使用当前代码),它不应该:
所以这是我当前抛出异常的代码(目前不处理“匹配”参数,我正在实现一个“等于”调用。会有其他人喜欢,开头等等)
异常只是其中一种“类型不匹配”,其函数无法作为参数传递给字符串等式或其他类型。
public static IQueryable<T> FilterString<T>(this IQueryable<T> query, Match match,
string criteriaItem, Expression<Func<T, string>> getItemString)
where T : class
{
if (string.IsNullOrEmpty(criteriaItem))
{
return query;
}
var param = Expression.Parameter(typeof(T), "r");
var selector = Expression.Lambda<Func<T, string>>(getItemString, param);
Expression<Func<string, bool>> prototype = item => item == criteriaItem;
var predicate = Expression.Lambda<Func<T, bool>>(
prototype.Body.ReplaceParameter(prototype.Parameters[0], selector.Body),
selector.Parameters[0]);
return query.Where(predicate);
}
和执行 sql 而不是仅仅生成它的那个
public static IQueryable<T> FilterString<T>(this IQueryable<T> query, Match match,
string criteriaItem, Expression<Func<T, string>> getItemString)
where T : class
{
if (string.IsNullOrEmpty(criteriaItem))
{
return query;
}
var param = Expression.Parameter(typeof(T), "r");
//var value = Expression.Constant(getItemString);
var equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
var item = Expression.Invoke(getItemString, param);
var body = Expression.Call(Expression.Constant(criteriaItem),
equals,
item);
return query.Where(Expression.Lambda<Func<T, bool>>(body, param));
}
像这样调用这些
query = query.FilterString(match, criteria_value, (r) => r.SomeProperty.MaybeSomeOtherProp.SomeString);
query = query.FilterString(match, criteria_value, (r) => r.SomeProperty.Name);
将在任意数量的不同实体上调用相同的扩展方法,具有不同的属性和道具名称。我想我可以利用我开始工作的反射版本,并在某种数组中传递所有 属性 名称,但这简直是丑陋。
长话短说,我怎样才能按照我上面解释的方式让它工作,那就是:生成 sql 而不是执行?
谢谢,
请注意,“ReplaceParameter”扩展方法来自此处:
所以,您正在尝试合并您的原型 item => item == criteriaItem
。使用传入的字符串 属性 表达式,例如 (r) => r.SomeProperty.Name
来创建 (r) => r.SomeProperty.Name == criteriaItem
.
Expression<Func<string, bool>> prototype = item => item == criteriaItem;
var predicate = Expression.Lambda<Func<T, bool>>(
ReplacingExpressionVisitor.Replace(
prototype.Parameters[0],
getItemString.Body,
prototype.Body),
getItemString.Parameters[0]);
而且我认为您正在尝试这样做,以便 criteriaItem
绑定到 sql 参数,而不是作为字符串常量内联。但是你的问题有点难以理解。