Fondo
Je dois pouvoir créer un fichier .include qui filtre les résultats en fonction des enregistrements créés à une date donnée ou avant. Je ne connaîtrai pas les types d'objets de l'instruction LINQ originale, et je les trouverai (en passant par tout un tas d'obstacles, mais cela fonctionne).
J'ai un graphique d'objets comme celui-ci :
A -> Ac
/ \
Bc <- B \
/ \
Cc <- C D -> Dc
Objets Ac
, Bc
, Cc
y Dc
sont toutes trouvées dynamiquement (c'est-à-dire qu'un développeur ne saisit pas ces relations lorsqu'il écrit l'instruction linq originale). Il faut ensuite les ajouter à une expression IQueryable et ne renvoyer que des valeurs allant jusqu'à une date précise.
Le code jusqu'à présent
J'ai essayé de construire une fonction qui construit un lambda et compare les dates. Jusqu'à présent, si la requête originale est A.Include(x => x.B).Include(x => x.B.Select(y => y.C)
je peux construire la chaîne "A.B.Bc"
Je connais donc les types de A, B et Bc, et la façon dont ils sont liés.
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);
}
Le problème est que le code ci-dessus se plante parce que "A.B.Bc.CreatedUtc"
n'est pas la bonne façon de charger la propriété de la date - je dois passer par le biais de .Select
déclarations.
Le problème
Afin de pouvoir à la fois carga les dossiers et filtre j'ai besoin de construire dynamiquement une .Select
pour extraire les valeurs dont j'ai besoin (qui, heureusement, sont statiques en raison de l'héritage) et les placer dans un type anonyme, qui peut ensuite être filtré avec une fonction .Where()
. Tout cela doit être fait avec un minimum de génériques, car je n'ai pas de types à valider à la compilation et je devrais abuser de la réflexion pour les faire fonctionner.
En gros, j'ai besoin de créer ceci :
.Select( x => new
{
Bc = x.B.Select(z => z.Bc.Select(y => y.CreatedUtc).Where( q => q > DateTime.UtcNow ) ),
A= x
} )
dynamiquement, en utilisant la chaîne de caractères "A.B.Bc", pour tout niveau d'imbrication.
Ressources
C'est ici que j'ai vu comment filtrer une méthode d'inclusion EF : LINQ To Entities Include + Méthode Where
Cet article parle de la création dynamique d'un Select, mais il ne sélectionne que les valeurs de haut niveau et ne semble pas pouvoir construire le reste des éléments nécessaires à mon problème : Comment créer un arbre d'expression LINQ pour sélectionner un type anonyme ?
Linq dynamique
À l'aide de la bibliothèque System.Linq.Dynamic, j'ai essayé d'accéder à la base de données de l'utilisateur. CreatedUtc
valeur de Bc :
query = (IQueryable<T>) query.Select(includeString + ".CreatedUtc");
Dónde includeString = "B.Bc"
mais cela me donne une exception de :
Exception thrown: 'System.Linq.Dynamic.ParseException' in System.Linq.Dynamic.dll
Additional information: No property or field 'Bc' exists in type 'List`1'