5 votes

Construire dynamiquement des inclusions avec le filtre Where

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'

2voto

Alexey Merson Points 323

Je crois System.Linq.Dynamic est ce dont vous avez besoin. Il s'agit d'une bibliothèque écrite à l'origine par ScottGu de Microsoft, qui vous permet de construire des requêtes LINQ à partir de chaînes de caractères comme celle-ci :

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

où la requête est IQueryable<A> .

Il existe un certain nombre de bifurcations de cette bibliothèque. Vous pouvez commencer par aquí . La documentation sur le langage de requête est aquí . Aussi aquí il existe une version avec quelques fonctionnalités supplémentaires et aquí est la version de dotnet.core.

UPD1 : Este La bibliothèque manque de la méthode d'extension SelectMany mais ce on a. Ainsi, avec cette dernière bibliothèque, vous pouvez faire ce qui suit :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X