动态构建包含在哪里过滤器

Max*_*Max 5 c# linq entity-framework

背景

我需要能够构建一个.Include,将结果过滤到特定日期或之前创建的记录.我不会知道原始LINQ语句中对象的类型,我将找到它们(通过大量的箍,但这是有效的).

我有一个像这样的对象图:

          A -> Ac
         /  \
  Bc <- B    \
       /      \  
Cc <- C        D -> Dc
Run Code Online (Sandbox Code Playgroud)

对象Ac,Bc,Cc,和Dc都是动态发现的(也就是写原来的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);
}
Run Code Online (Sandbox Code Playgroud)

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

问题

为了既加载的记录和过滤他们,我需要动态地构建一个.Select拉出来,我需要的值(其中,幸运的是,是静态的基础上继承),并把这些值转换成一个匿名类型,然后可以用过滤a .Where().所有这些都需要用最少的泛型完成,因为我没有编译时类型来验证,我不得不滥用反射来让它们工作.

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

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

动态地,使用字符串"ABBc",用于任何级别的嵌套.

资源

这是我看到如何过滤EF包含方法的地方: LINQ To Entities Include + Where Method

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

动态Linq

使用System.Linq.Dynamic库,我试图访问CreatedUtcBc 的值:

query = (IQueryable<T>) query.Select(includeString + ".CreatedUtc");
Run Code Online (Sandbox Code Playgroud)

哪里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'
Run Code Online (Sandbox Code Playgroud)

Ale*_*son 2

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

IQueryable nestedQuery = query.Select("B.Bc.CreatedUtc");
Run Code Online (Sandbox Code Playgroud)

其中查询是IQueryable<A>.

这个库有很多分支。你可以从这里开始。有关查询语言的文档位于此处这里还有一个带有一些附加功能的版本,这里是 dotnet.core 版本。

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

query.SelectMany("B").SelectMany("Bc").Select("CreatedUtc");
Run Code Online (Sandbox Code Playgroud)