我遇到了一个问题,这对我来说是个新问题
我有以下实体(我使用流利的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
我想做的就是根据2个过滤器做一些过滤操作:
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
内部使用该方法{1}}方法。
像
这样的东西 var query = query.Where(firstSwitchFilterMethod && secondFilterMethod)
伪代码:
firstSwitchFilterMethod:
if (firstFilter.@operator == "Contains")
return SomeEntity.Name.Contains(firstFilter.value);
依旧......
有什么想法吗?我正在考虑Expression<Func<>>
- 这是好方向吗?如果是这样,如何在我的情况下使用它?
或者,也许可以构建我自己的SomeEntity
扩展方法,它将使用该过滤器类?
答案 0 :(得分:2)
您不能创建任意函数调用表达式并期望它可以转换为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");
答案 1 :(得分:0)
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))
...
}