Domanda

Devo filtrare gli elementi figlio di un'entità in linq usando una singola query linq. È possibile?

Supponiamo di avere due tabelle correlate. Versi e VerseTraduzioni. L'entità creata da LINQ to SQL è tale che ho un oggetto Verse che contiene un oggetto figlio che è una raccolta di VerseTranslation.

Ora, se ho la seguente query linq

var res = from v in dc.Verses
                  where v.id = 1
                  select v;

Ottengo una raccolta di versetti il ??cui ID è 1 e ogni oggetto verso contiene tutti gli oggetti figlio di VerseTranslations.

Quello che voglio anche fare è filtrare quell'elenco figlio di Verse Translations.

Finora l'unico modo in cui sono stato in grado di inventarmi è usare un nuovo tipo Anonimo o altro. Come segue

var res= from v in dc.Verses
                   select new myType
                   {
                       VerseId = v.VerseId,
                       VText = v.Text,
                       VerseTranslations = (from trans in v.VerseTranslations
                                               where languageId==trans.LanguageId
                                               select trans
                   };

Il codice sopra funziona, ma ho dovuto dichiarare una nuova classe per questo. Non c'è modo di farlo in modo tale che il filtro sulla tabella figlio possa essere incorporato nella prima query linq in modo che nessuna nuova classe debba essere dichiarata.

Saluti, MAC

È stato utile?

Soluzione

Quindi finalmente sono riuscito a farlo funzionare grazie ai suggerimenti forniti da Shiraz.

        DataLoadOptions options = new DataLoadOptions();
        options.AssociateWith<Verse>(item => item.VerseTranslation.Where(t => languageId.Contains(t.LanguageId)));

        dc.LoadOptions = options;

        var res = from s in dc.Verse
                   select s;

Ciò non richiede la proiezione o l'utilizzo di nuove classi di estensione.

Grazie per tutte le persone che hai inserito.

Altri suggerimenti

Filtro sulla raccolta racchiusa dell'oggetto,

var res = dc.Verses
            .Update(v => v.VerseTranslations 
                      =  v.VerseTranslations
                          .Where(n => n.LanguageId == languageId));

Utilizzando il metodo di estensione " Aggiorna " da HookedOnLinq

public static class UpdateExtensions {

    public delegate void Func<TArg0>(TArg0 element);

    /// <summary>
    /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="update">The update statement to execute for each element.</param>
    /// <returns>The numer of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update) {
        if (source == null) throw new ArgumentNullException("source");
        if (update == null) throw new ArgumentNullException("update");
        if (typeof(TSource).IsValueType) 
            throw new NotSupportedException("value type elements are not supported by update.");

        int count = 0;
        foreach(TSource element in source) {
            update(element);
            count++;
        }
        return count;
    }
}

Se proviene da un database, è possibile eseguire la prima istruzione.

Quindi esegui un caricamento o inclusione di VerseTranslations con una clausola Where.

http://msdn.microsoft.com/en-us/library /bb896249.aspx

Hai una relazione nel tuo modello tra Verse e VerseTranslations. In tal caso potrebbe funzionare:

var res= from v in 
dc.Verses.Include("VerseTranslations").Where(o => languageId==o.LanguageId)
select v;
  

Non c'è modo di farlo in un tale   modo tale che il filtro sul   tabella figlio può essere incorporata in   prima query linq in modo che nessuna novità   le classi devono essere dichiarate?

Tecnicamente, la risposta è no. Se stai cercando di restituire più dati di quanti un oggetto di una singola entità (Verse, VerseTranslation) può contenere, avrai bisogno di una sorta di oggetto per " progetto " in. Tuttavia, puoi aggirare dichiarando esplicitamente myType utilizzando un tipo anonimo:

var res = from v in dc.Verses
          select new
          {
              Verse = v,
              Translations = (from trans in v.VerseTranslations
                              where languageId==trans.LanguageId
                              select trans).ToList()
          };

var first = res.First();
Console.WriteLine("Verse {0} has {1} translation(s) in language {2}.",
    first.Verse.VerseId, first.Translations.Count, languageId);

Il compilatore genererà una classe con proprietà Verse e Translations digitate in modo appropriato. È possibile utilizzare questi oggetti praticamente per tutto il tempo in cui non è necessario fare riferimento al tipo in base al nome (ad esempio per tornare da un metodo denominato). Quindi, mentre non stai tecnicamente "dichiarando" un tipo, stai ancora utilizzando un nuovo tipo che verrà generato in base alle tue specifiche.

Per quanto riguarda l'utilizzo di una singola query LINQ, tutto dipende da come si desidera strutturare i dati. Per me sembra che la tua query originale abbia più senso: accoppia ogni Verse con un elenco filtrato di traduzioni. Se ti aspetti una sola traduzione per lingua, puoi utilizzare SingleOrDefault (o FirstOrDefault ) per appiattire la tua subquery o semplicemente usare un SelectMany come in questo modo:

var res= from v in dc.Verses
         from t in v.VerseTranslations.DefaultIfEmpty()
         where t == null || languageId == t.LanguageId
         select new { Verse = v, Translation = t };

Se un verso ha più traduzioni, questo restituirà una "riga" per ogni coppia Verse / Translation. Uso DefaultIfEmpty () come join sinistro per essere sicuro che avremo tutti i versetti anche se manca una traduzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top