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'