Enable Dynamic Build Using the Where Filter

Background

I need to be able to create .Include, which filters the results to records created on or before a specific date. I will not know the types of objects in the original LINQ instruction, and I will find them (through many hoops, but this works).

I have a graph of such objects:

A -> Ac / \ Bc <- B \ / \ Cc <- CD -> Dc 

The objects Ac , Bc , Cc and Dc all found dynamically (that is, the developer does not introduce these relations when writing the original linq expression). They should then be added to the IQueryable expression and return values ​​up to a specific DateTime value.

Code still

I tried to create a function that builds lambda and compare dates. For now, if the original request is A.Include(x => xB).Include(x => xBSelect(y => yC) , I can build the string "ABBc" , so I know the types A, B and Bc and how they connected.

 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); } 

The problem is that the above code crashes because "ABBc.CreatedUtc" not the right way to load the date property - I need to go through the .Select instructions.

Problem

To load records and filter them, I need to dynamically build .Select to pull out the values ​​I need (which, fortunately, are static based on inheritance) and put these values ​​in an anonymous type, which can then be filtered with .Where() . All this needs to be done with minimal generics, since I don't have compile time types to test, and I will have to abuse my thoughts to get them to work.

Essentially, I need to create this:

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

dynamically using the string "ABBc" for any level of nesting.

Resources

Here's where I saw how to filter the EF include method: LINQ To Entities Include + Where Method

This post talks about dynamically creating Select, but it only selects top-level values ​​and it doesn't seem like it can build the rest of the parts needed for my problem: How to create a LINQ expression tree to select an anonymous type

Dynamic linq

Using the System.Linq.Dynamic library, I tried to access the value of CreatedUtc off Bc:

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

Where includeString = "B.Bc" , but this gives me an exception:

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

I believe that System.Linq.Dynamic is what you need. This is a library originally written by ScottGu from Microsoft, and it allows you to build a LINQ query from this line:

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

where is the IQueryable<A> query.

There are several forks of this library. You can start from here . The query language documentation is here . There is also a version with some additional functionality, and here is the version of dotnet.core.

UPD1: There is no SelectMany selection method in this library, but this . So with the latest library you can do the following:

 query.SelectMany("B").SelectMany("Bc").Select("CreatedUtc"); 
+2
source

All Articles