Pourquoi cette déclaration de jointure LINQ ne fonctionne-t-elle pas?

c# entity-framework join linq linq-to-entities

Question

J'ai cette requête LINQ:

    // types...
    LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>();

    var result = from i in _ctx.Items
                 join s in itemScores on i.Id equals s._id
                 orderby s._score descending
                 select new ItemSearchResult(i, s._score);

    // this fails:
    return result.ToList();

Qui génère cette erreur:

Impossible de créer une valeur constante de type 'System.Collections.Generic.IEnumerable`1'.
Seuls les types primitifs ('tels que Int32, String et Guid') sont pris en charge dans ce contexte.

[EDIT] Voici le code de WeightedItem :

public class WeightedItem
{
    public int _id;
    public decimal? _score;

    public WeightedItem(int id, decimal? score)
    {
        _id = id;
        _score = score;
    }
}

Pouvez-vous voir ce que j'ai mal fait? Le code est parfaitement compilé et les objets _ctx.Items et itemScores contiennent les valeurs appropriées.

Réponse acceptée

Oui, cela compilerait bien - le problème est qu’il ne peut pas le traduire en SQL. Lorsque vous référencez des valeurs "locales", le cadre de l'entité doit déterminer quoi en faire lorsqu'il doit créer une requête SQL. Il est fondamentalement impossible de faire une jointure entre une collection en mémoire et une table de base de données.

Une chose qui pourrait fonctionner serait d'utiliser Contains place. Je ne sais pas si LinkedList<T> fonctionnera pour cela, mais je pense que List<T> fonctionne, du moins dans LINQ to SQL:

List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList();

var tmp = (from i in _ctx.Items
           where requiredScoreIds.Contains(i.Id)
           orderby s._score descending
           select i).AsEnumerable();

// Now do the join in memory to get the score
var result = from i in tmp
             join s in itemScores on i.Id equals s._id
             select new ItemSearchResult(i, s._score);

Cela fait maintenant une jointure dans la requête en mémoire, ce qui est quelque peu inutile. Vous pouvez utiliser un dictionnaire à la place:

List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList();

var tmp = (from i in _ctx.Items
           where requiredScoreIds.Contains(i.Id)
           orderby s._score descending
           select i).AsEnumerable();

// Create a map from score ID to actual score
Dictionary<int, decimal?> map = itemScores.ToDictionary(x => x._id,
                                                        x => x._score);

var result = tmp.Select(i => new ItemSearchResult(i, map[i.Id]));

Réponse populaire

Vous ne pouvez pas joindre une liste en mémoire à un objet interrogeable. Vous devez faire quelque chose comme ça:

var criteria = itemScores.Select(x => x._id).ToList();
var result_tag = (from i in _ctx.Items
                 where criteria.Contains(i.ID)
                 select i).ToList();
var result = from i in result_tag
             join s in itemScores on i.ID equals s._id
             orderby s._score descending
             select new ItemSearchResult(i, s._score);


Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow