IQueryable和自定义过滤 - 使用Expression <func <>&gt;?

时间:2015-10-24 11:24:14

标签: c# nhibernate filtering

我遇到了一个问题,这对我来说是个新问题

我有以下实体(我使用流利的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扩展方法,它将使用该过滤器类?

2 个答案:

答案 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))
     ...
}