可查询和自定义过滤-使用Expression>
本文关键字:Expression Func 使用 查询 自定义 过滤 | 更新日期: 2023-09-27 18:19:06
我面临一个新问题
我有以下实体(我使用流利的nhibernate,但这里无关紧要)
public class SomeEntity
{
public virtual string Name { get; set; }
}
过滤器类:public class FilterOptions
{
public string logic { get; set; } // "and", "or"
public FilterItems[] filters { get; set; }
}
public class FilterItems
{
public string @operator { get; set; }
public string value { get; set; } //string value provided by user
}
@operator
属性可以有以下值
EndsWith
DoesNotContain
Contains
StartsWith
NotEqual
IsEqualTo
我要做的就是基于两个过滤器做一些过滤操作:
private IQueryable<SomeEntity> BuildQuery(FilterOptions opts)
{
IQueryable<SomeEntity> query = Session.Query<SomeEntity>();
var firstFilter = opts.filters[0];
var secondFilter = opts.filters[1];
}
因为@operator
属性可以有这么多选项,我想知道是否有可能使用swich
操作符的外部方法,并在.Where
方法内部使用该方法。
var query = query.Where(firstSwitchFilterMethod && secondFilterMethod)
伪代码:
firstSwitchFilterMethod:
if (firstFilter.@operator == "Contains")
return SomeEntity.Name.Contains(firstFilter.value);
等等…
有什么想法吗?我在考虑Expression<Func<>>
——它的方向好吗?如果是的话,在我的情况下如何使用它?
或者,也许建立自己的SomeEntity
的扩展方法,这将使用该过滤器类?
您不能任意调用函数表达式并期望它可以转换为SQL。但是有一些函数,比如StartsWith,可以。下面是一个例子,教你如何构建自己的表达式:
protected IQueryable<T> GetFiltered<T>(IQueryable<T> query, string filterOnProperty, string startsWithString, string endsWithString)
{
LambdaExpression startsWithLambda = (Expression<Func<string, string, bool>>)((x, s) => x.StartsWith(s));
MethodInfo startsWithMI = (startsWithLambda.Body as MethodCallExpression).Method;
LambdaExpression endsWithLambda = (Expression<Func<string, string, bool>>)((x, s) => x.EndsWith(s));
MethodInfo endsWithMI = (endsWithLambda.Body as MethodCallExpression).Method;
ParameterExpression param = Expression.Parameter(typeof(T));
Expression nameProp = Expression.Property(param, filterOnProperty);
Expression filteredOk = Expression.Constant(true);
Expression startsWithStringExpr = Expression.Constant(startsWithString);
Expression startsWithCondition = Expression.Call(nameProp, startsWithMI, startsWithStringExpr);
filteredOk = Expression.AndAlso(filteredOk, startsWithCondition);
Expression endsWithStringExpr = Expression.Constant(endsWithString);
Expression endsWithCondition = Expression.Call(nameProp, endsWithMI, endsWithStringExpr);
filteredOk = Expression.AndAlso(filteredOk, endsWithCondition);
Expression<Func<T, bool>> where = Expression.Lambda<Func<T, bool>>(filteredOk, new ParameterExpression[] { param });
return query.Where(where);
}
用法简单
DCDataContext dc = new DCDataContext();
var query = dc.testtables.AsQueryable();
query = GetFiltered(query, "name", "aaa", "2");
NHibernate或任何其他LINQ提供程序并不真正关心你如何构建表达式-唯一重要的是最终表达式只包含LINQ提供程序理解并知道如何处理的结构。
是的,你可以让一个方法返回一个Expression<Func<>>
,然后使用Where()
方法将该表达式添加到LINQ查询中。
然而,你不能要求NHibernate分析你编译的代码并将if语句转换为SQL。您的方法将需要分析选项并返回一个合适的表达式,该表达式将插入到完整的LINQ查询中。
你可以在一个方法中这样写:
IQueryable<SomeEntity> query = Session.Query<SomeEntity>();
if (isEqOp)
query = query.Where(e => e.Name == options.Value)
if (isContainsOp)
query = query.Where(e => e.Name.Contains(options.Value))
query.ToList();
或者如果你想在一个单独的方法中使用过滤器逻辑,你也可以像这样分割它:
public Expression<Func<Entity, bool>> GetExpression(options)
{
if (isEqOp)
return (Entity e) => e.Name == options.Value;
if (isContainsOp)
return (Entity e) => e.Name.Contains(options.Value);
}
{
IQueryable<SomeEntity> query = Session.Query<SomeEntity>();
query = query.Where(GetExpression(options))
...
}