使用 Func<T, string> lambda 动态构造 where 子句 - linq 到实体
Posted
技术标签:
【中文标题】使用 Func<T, string> lambda 动态构造 where 子句 - linq 到实体【英文标题】:Dynamically construct where clause with Func<T, string> lambda - linq to entities 【发布时间】:2021-06-19 14:24:03 【问题描述】:(适用于 .Net Framework 4.7)
我正在尝试编写一些扩展方法来帮助为各种实体创建动态 where 子句。我是几天前开始的,所以可能有很多我不知道的地方,还有一些我可能误解了。
我已经设法创建了一个扩展方法,用于按 1 个按预期工作的属性进行过滤(我确实使用反射来获取该属性,但无法让它与接口一起工作 - 好吧,没有它执行 sql那是)。 不过,我似乎无法让这个用于 lambda 表达式。
注意,解决方案一定不能触发sql执行。因为我能够写出一些“有效”的变体,但它们会触发 sql 执行。
我的工作方式是,一旦我准备好代码,我就开始调试并在手表中使用“查询”。它看起来像这样(注意 sql 代码)
一旦我跳过我的 FilterString 方法调用,它要么变成一个 sql 结果,要么我得到一个异常(使用当前代码),它不应该:
这是我当前抛出异常的代码(目前不处理“匹配”参数,我正在实现“等于”调用。还会有其他类似、开头为、类似等)
例外只是具有函数的“类型不匹配”之一不能作为参数传递给字符串 Equals 或其他什么。
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”扩展方法是来自这里:https://***.com/a/39206392/630515
【问题讨论】:
我没有找到任何问号。那么真正的问题是什么?您在寻找代码审查吗? Match 参数的用途是什么?我没有看到它在任何地方使用? @Marco 我解释了我的问题中缺少它的原因。请仔细阅读。谢谢 @JeroenvanLangen 抱歉,我将此作为一个问题提出,“虽然我似乎无法让这个用于 lambda 表达式。”,但我很高兴以一个问题,所以它更清楚。谢谢 【参考方案1】:所以,您正在尝试合并您的原型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 参数,而不是作为字符串常量内联。但是你的问题有点难以理解。
【讨论】:
抱歉问题不清楚。希望通过添加问题更清楚?另外,我认为 EF Core 在这方面与我们的 .net 框架没有什么不同,但是看到 ReplacingExpressionVisitor 仅存在于 .net 5 中,我还注意到这是针对 v4.7 同时我想看看是否有人写过我可以使用的类似方法(或者你可能会为我指出正确的方向?)谢谢 我使用了上面提到的 ReplaceParameter ext 方法而不是 .net5 的 ReplacingExpressionVisitor 并将您的建议调整到我的代码中,我得到了它的工作,谢谢以上是关于使用 Func<T, string> lambda 动态构造 where 子句 - linq 到实体的主要内容,如果未能解决你的问题,请参考以下文章
将 .net Func<T> 转换为 .net Expression<Func<T>>
Expression<Func<T,TResult>>和Func<T,TResult>