使用Where筛选器动态生成Include

本文关键字:Include 动态 Where 筛选 使用 | 更新日期: 2023-09-27 17:54:57

背景

我需要能够建立一个。Include,用于筛选在特定日期或之前创建的记录的结果。我不知道原始LINQ语句中对象的类型,我会找到它们(通过很多困难,但这是有效的(。

我有一张这样的对象图:

          A -> Ac
         /  '
  Bc <- B    '
       /      '  
Cc <- C        D -> Dc

对象AcBcCcDc都是动态找到的(也就是说,开发人员在编写原始linq语句时不会键入这些关系(。然后需要将这些值添加到IQueryable表达式中,并且只返回特定DateTime之前的值。

迄今为止的代码

我尝试构建一个函数来构造lambda并比较日期。到目前为止,如果原始查询是A.Include(x => x.B).Include(x => x.B.Select(y => y.C),我可以构造字符串"A.B.Bc",这样我就知道了A、B和Bc的类型以及它们之间的关系。

private static IQueryable<T> FilterChangeTrackerToDate<T>(this IQueryable<T> query, string includeString, DateTime targetDateTime)
{
    var param = Expression.Parameter( typeof( T ), "x" );
    var prop = Expression.Property(param, includeString + ".CreatedUtc");
    var dateConst = Expression.Constant(targetDateTime);
    var compare = Expression.LessThanOrEqual(prop, dateConst);
    var lambda = Expression.Lambda<Func<T, bool>>(compare, param);
    return query.Where(lambda);
}

问题是上面的代码崩溃了,因为"A.B.Bc.CreatedUtc"不是加载日期属性的正确方式——我需要通过.Select语句遍历它。

问题

为了加载记录并过滤它们,我需要动态构建一个.Select来提取我需要的值(谢天谢地,这些值是基于继承的静态值(,并将这些值放入一个匿名类型中,然后可以使用.Where()对其进行过滤。所有这些都需要用最少的泛型来完成,因为我没有可验证的编译时类型,而且我必须滥用反射才能使它们发挥作用。

本质上,我需要创建这个:

.Select( x => new
{
    Bc = x.B.Select(z => z.Bc.Select(y => y.CreatedUtc).Where( q => q > DateTime.UtcNow ) ),
    A= x
} )

动态,使用字符串"A.B.Bc",用于任何级别的嵌套。

资源

这就是我看到如何过滤EF include方法的地方:LINQ To实体包括+Where方法

这篇文章讨论了动态创建Select,但它只是选择顶级值,似乎无法构建我的问题所需的其余部分:如何创建LINQ表达式树来选择匿名类型

动态Linq

使用系统。林克。动态库,我尝试访问Bc:的CreatedUtc

query = (IQueryable<T>) query.Select(includeString + ".CreatedUtc");

其中includeString = "B.Bc",但这给了我一个例外:

Exception thrown: 'System.Linq.Dynamic.ParseException' in System.Linq.Dynamic.dll
Additional information: No property or field 'Bc' exists in type 'List`1'

使用Where筛选器动态生成Include

我相信System.Linq.Dynamic正是您所需要的。这是一个库,最初由微软的ScottGu编写,它允许您从以下字符串构建LINQ查询:

IQueryable nestedQuery = query.Select("B.Bc.CreatedUtc");

其中查询为CCD_ 14。

这个图书馆有许多岔路口。你可以从这里开始。关于查询语言的文档在这里。这里还有一个带有一些附加功能的版本,这里是dotnet.core版本。

UPD1:这个库缺少SelectMany扩展方法,但这个库有。因此,使用后一个库,您可以执行以下操作:

query.SelectMany("B").SelectMany("Bc").Select("CreatedUtc");