Pergunta

Eu preciso para filtrar os elementos filho de uma entidade em LINQ usando uma única consulta LINQ. Isso é possível?

Suponha que eu tenho duas tabelas relacionadas. Versos e VerseTranslations. A entidade criada pelo LINQ to SQL é tal que eu tenho um objeto verso que contém um objeto filho que é uma coleção de VerseTranslation.

Agora, se eu tenho o acompanhamento linq consulta

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

I obter uma coleção de versos cuja id é 1 e cada objeto verso contém todos os objetos filho de VerseTranslations.

O que eu também quero fazer é filtro que lista de filhos do verso Translations.

Até agora, a única maneira que eu fui capaz de vir acima com é usando um novo tipo anônimo ou de outra forma. Como se 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
                   };

O código acima funciona, mas eu tinha que declarar uma nova classe para ele. Não há nenhuma maneira de fazê-lo de tal maneira que a filtragem na tabela filho pode ser incorporada na primeira consulta LINQ para que nenhum novas classes têm de ser declaradas.

Saudações, MAC

Foi útil?

Solução

Então, eu finalmente consegui-lo para trabalhar graças aos ponteiros dados por 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;

Este não necessita de projeção ou usando novas classes de extensão.

Obrigado por todas as suas entradas pessoas.

Outras dicas

A filtragem na coleção fechado do objeto,

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

Ao usar o método de extensão "Update" do 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 este é proveniente de um banco de dados, você pode executar a sua primeira declaração.

Em seguida, fazer uma carga ou Incluir de VerseTranslations com uma cláusula WHERE.

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

Você tem um relacionamento no seu modelo entre verso e VerseTranslations. Nesse caso, este trabalho poder:

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

Não há nenhuma maneira de fazê-lo em tal forma tal que a filtragem no tabela criança pode ser incorporado no primeira consulta linq para que nenhuma nova aulas têm de ser declaradas?

Tecnicamente, a resposta é não. Se você está tentando retornar mais dados do que um único objeto de entidade (Verso, VerseTranslation) pode segurar, você vai precisar de algum tipo de objeto para "projeto" em. No entanto, você pode obter em torno explicitamente declarando myType usando um tipo anônimo:

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

O compilador irá gerar uma classe com propriedades verso e Traduções adequadamente tipado para você. Você pode usar esses objetos para praticamente qualquer coisa, contanto que você não precisa para se referir ao tipo por nome (para retornar de um método chamado, por exemplo). Então, enquanto você não está tecnicamente "declarando" um tipo, você ainda está usando um novo tipo que será gerado por sua especificação.

Tanto quanto usando uma única consulta LINQ, tudo depende de como você deseja que os dados estruturados. Para mim parece que a sua consulta original faz mais sentido: emparelhar cada Verse com uma lista filtrada de traduções. Se você espera apenas uma única tradução por língua, você poderia usar SingleOrDefault (ou FirstOrDefault) para nivelar o subconsulta, ou apenas usar um SelectMany assim:

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 um verso tem várias traduções, isso irá retornar uma "linha" para cada par Verso / Tradução. Eu uso DefaultIfEmpty() como um LEFT JOIN para se certificar de que terá todos os versos, mesmo se eles estão faltando uma tradução.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top