【问题标题】:Expression Trees: Filtered count on navigation property表达式树:导航属性的过滤计数
【发布时间】:2016-11-15 01:40:40
【问题描述】:

我正在制作一个动态报表构建器,它允许用户从预定义的类(通过实体框架映射到数据库表)中选择字段作为其数据的过滤器。为了构建我的 LINQ to Entities 查询,由于查询的动态特性,我使用了表达式树。我让它适用于几乎所有非自定义场景,但我真的很难让它适用于一些自定义场景。

我的一个自定义查询的模型的缩写版本如下所示:

public class Attendee {
    public int ID { get; set; }
    public DateTime? CancelledOn { get; set; }

    [ForeignKey("Event")]
    public int Event_ID { get; set; }
    public virtual Event Event { get; set; }        
}

public class Event {
    public int ID { get; set; }
    public virtual ICollection<Attendee> Attendees { get; set; }        
}

用户想要运行的示例查询是过滤以仅显示未取消的参与者超过 10 人的事件。如果我在一个普通的 IQueryable 查询中写这个,我会把它写成

db.Event.Where(s => s.Attendees.Count(a => a.CancelledOn == null) > 10);

使用我设置的表达式树框架,我已经可以处理“> 10”部分,但我不知道如何动态生成“s.Attendees.Count(a => a.CancelledOn == null) “ 部分。我已经阅读了关于在顶级属性上执行计数或求和的 SO 帖子,但我无法破解任何这些解决方案来为过滤的导航属性工作。示例帖子:Dynamic LINQ, Select function, works on Enumerable, but not Queryable

下面的屏幕截图是使用表达式树构建的不同过滤器的示例,因此您可以看到我正在工作的示例。 “pe”是传入的类型“Event”的ParameterExpression。 “表达”是我试图创造和评估的。 http://grab.by/RoYm

上面正在运行的 LINQ 查询是

db.Event.Where(s=> s.StartDate >= '1/1/2016 12:00 am')

我们将不胜感激任何帮助或指导,如果我需要包含任何其他代码 sn-ps,请告诉我。

【问题讨论】:

  • 这是一个什么样的项目?如果是 MVC/Web API,你真的应该考虑 ODATA(我之前做过和你一样的事情,结果维护起来很痛苦)
  • @SteveG,ASP.NET MVC。 ODATA 是否接受字符串值作为过滤器和选择的输入?用户可以选择数百个字段进行过滤和显示,他们还可以使用分组、AND/OR'ing、各种运算符(为空、为空、等于、包含、大于等)构建复杂的过滤器.).
  • @ZachFloyd:是的,这正是 ODATA 的用途。您可以纯粹通过 http 查询字符串创建相当复杂的过滤器和选择,Web API 的 OData 实现将为您将它们应用于 IQueryables。

标签: c# asp.net-mvc linq expression-trees


【解决方案1】:

为了为Count 生成Expression 树,您应该为对应的Method 生成一个Call

这是代码的初稿......

Expression callExpr = Expression.Call(
    Expression.Constant(s.Attendees), 
    typeof(ICollection<Attendee>).GetMethod("get_Count")); // + 2 Arguments

当然,你必须更详细地把它合并到你的主程序中。

一个基本的用法示例是

// Print out the expression.
Debug.WriteLine(callExpr.ToString());

// The following statement first creates an expression tree,
// then compiles it, and then executes it.  
Debug.WriteLine(Expression.Lambda<Func<int>>(callExpr).Compile()());

最后,Count Expression (NodeType Call) 必须包含 2 个参数(作为第三个参数,上面未显示):

  • 收藏
  • Lambdaa =&gt; a.CancelledOn == null

【讨论】:

    【解决方案2】:

    不确定您正在寻找的方法的输入参数是什么,但以下内容应该为您提供一个起点。基本部分是构建对Enumerable.Count(predicate) 方法的方法调用。

    static Expression<Func<TSource, bool>> MakeCountPredicate<TSource>(string collectionName, string itemName, ExpressionType itemComparison, string itemValue, ExpressionType countComparison, int countValue)
    {
        var source = Expression.Parameter(typeof(TSource), "s");
        var collection = Expression.Property(source, collectionName);
        var itemType = collection.Type.GetInterfaces()
            .Single(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            .GetGenericArguments()[0];
        var item = Expression.Parameter(itemType, "e");
        var itemProperty = Expression.Property(item, itemName);
        var itemPredicate = Expression.Lambda(
            Expression.MakeBinary(itemComparison, itemProperty, Expression.Constant(
                string.IsNullOrEmpty(itemValue) || itemValue.Equals("null", StringComparison.OrdinalIgnoreCase) ? null :
                Convert.ChangeType(itemValue, itemProperty.Type))),
            item);
        var itemCount = Expression.Call(
            typeof(Enumerable), "Count", new[] { itemType },
            collection, itemPredicate);
        var predicate = Expression.Lambda<Func<TSource, bool>>(
            Expression.MakeBinary(countComparison, itemCount, Expression.Constant(countValue)),
            source);
        return predicate;
    }
    

    所以示例谓词表达式

    Expression<Func<Event, bool>> predicate =
        s => s.Attendees.Count(a => a.CancelledOn == null) > 10
    

    可以像这样动态构建

    var predicate = MakeCountPredicate<Event>("Attendees", 
        "CancelledOn", ExpressionType.Equal, "null", ExpressionType.GreaterThan, 10);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-28
      • 1970-01-01
      • 2013-01-14
      • 2012-02-17
      • 1970-01-01
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      相关资源
      最近更新 更多