Arbre d'expression LINQ N'importe quel () à l'intérieur Où ()

c# expression-trees lambda linq

Question

J'essaie de générer la requête LINQ suivante:

//Query the database for all AdAccountAlerts that haven't had notifications sent out
//Then get the entity (AdAccount) the alert pertains to, and find all accounts that
//are subscribing to alerts on that entity.
var x = dataContext.Alerts.Where(a => a.NotificationsSent == null)
  .OfType<AdAccountAlert>()
  .ToList()
  .GroupJoin(dataContext.AlertSubscriptions,
    a => new Tuple<int, string>(a.AdAccountId, typeof(AdAccount).Name),
    s => new Tuple<int, string>(s.EntityId, s.EntityType),
    (Alert, Subscribers) => new Tuple<AdAccountAlert, IEnumerable<AlertSubscription>> (Alert, Subscribers))
  .Where(s => s.Item2.Any())
  .ToDictionary(kvp => (Alert)kvp.Item1, kvp => kvp.Item2.Select(s => s.Username));

Utilisation des arbres d'expression (ce qui semble être le seul moyen de le faire lorsque j'ai besoin d'utiliser des types de réflexion et d'exécution). Notez que dans le code réel (voir ci-dessous), AdAccountAlert est en réalité dynamique par réflexion et par une boucle for.

Mon problème : je peux tout générer jusqu'à la clause .Where (). L'appel à la méthode whereExpression explose en raison de types incompatibles. Normalement, je sais quoi mettre là, mais l'appel de la méthode Any () m'a confondu. J'ai essayé tous les types auxquels je peux penser et pas de chance. Toute aide à la fois avec .Where () et .ToDictionary () serait appréciée.

Voici ce que j'ai jusqu'à présent:

//Query the database for all AdAccountAlerts that haven't had notifications sent out
//Then get the entity (AdAccount) the alert pertains to, and find all accounts that
//are subscribing to alerts on that entity.
var x = dataContext.Alerts.Where(a => a.NotificationsSent == null)
  .OfType<AdAccountAlert>()
  .ToList()
  .GroupJoin(dataContext.AlertSubscriptions,
    a => new Tuple<int, string>(a.AdAccountId, typeof(AdAccount).Name),
    s => new Tuple<int, string>(s.EntityId, s.EntityType),
    (Alert, Subscribers) => new Tuple<AdAccountAlert, IEnumerable<AlertSubscription>> (Alert, Subscribers))
  .Where(s => s.Item2.Any())
  .ToDictionary(kvp => (Alert)kvp.Item1, kvp => kvp.Item2.Select(s => s.Username));

Réponse populaire

Remarque: tout ce qui suit et incluant ToList() ne fonctionnera pas sur IQueryable<T> mais sur IEnumerable<T> . Pour cette raison, il n'est pas nécessaire de créer des arbres d'expression. Ce n'est certainement pas ce qui est interprété par EF ou similaire.

Si vous examinez le code généré par le compilateur pour votre requête d'origine, vous constaterez qu'il génère des arbres d'expression uniquement avant le premier appel à ToList .

Exemple:

Le code suivant:

var query = new List<int>().AsQueryable();
query.Where(x => x > 0).ToList().FirstOrDefault(x => x > 10);

Est traduit par le compilateur à ceci:

var query = new List<int>().AsQueryable();
query.Where(x => x > 0).ToList().FirstOrDefault(x => x > 10);

Notez s'il vous plaît comment il génère des expressions pour tout jusqu'à ToList . Tout ce qui suit et tout ce qui précède sont simplement des appels normaux à des méthodes d’extension.

Si vous n'imitez pas cela dans votre code, vous enverrez en fait un appel à Enumerable.ToList au fournisseur LINQ - qu'il tente ensuite de convertir en SQL et échoue.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi