Perché questa istruzione di join LINQ non funzionerà?

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

Domanda

Ho questa query 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();

Che sta generando questo errore:

Impossibile creare un valore costante di tipo 'System.Collections.Generic.IEnumerable`1'.
Solo i tipi primitivi ('come Int32, String e Guid') sono supportati in questo contesto.

[EDIT] Ecco il codice di WeightedItem :

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

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

Riesci a vedere cosa ho fatto di sbagliato? Il codice viene compilato perfettamente e sia _ctx.Items che itemScores contengono valori appropriati.

Risposta accettata

Sì, si compila bene - il problema è che non può tradurlo in SQL. Quando si fa riferimento a valori "locali", il framework di entità deve capire cosa fare con loro quando è necessario creare una query SQL. Fondamentalmente non può far fronte a un join tra una raccolta in memoria e una tabella di database.

Una cosa che potrebbe funzionare sarebbe utilizzare invece Contains . Non so se LinkedList<T> funzionerà per questo, ma credo che List<T> , almeno in 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);

Ora sta facendo un join nella query in memoria, che è in qualche modo superfluo. Puoi invece usare un dizionario:

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]));

Risposta popolare

Non è possibile unire tra un elenco in memoria e un oggetto queryble. Devi fare qualcosa del genere:

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

Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché