Pregunta

Necesito filtrar los elementos secundarios de una entidad en linq usando una sola consulta de linq. es posible?

Supongamos que tengo dos tablas relacionadas. Versos y VerseTranslations. La entidad creada por LINQ to SQL es tal que tengo un objeto Verse que contiene un objeto hijo que es una colección de VerseTranslation.

Ahora si tengo la siguiente consulta de linq

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

Obtengo una colección de Versos cuya identificación es 1 y cada objeto de verso contiene todos los objetos secundarios de VerseTranslations.

Lo que también quiero hacer es filtrar esa lista secundaria de Traducciones de Verse.

Hasta ahora, la única forma en que he sido capaz de encontrarlo es mediante el uso de un nuevo Tipo Anónimo o de otra manera. De la siguiente manera

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

El código anterior funciona, pero tuve que declarar una nueva clase para él. ¿No hay forma de hacerlo de tal manera que el filtrado en la tabla secundaria se pueda incorporar en la primera consulta linq para que no se tengan que declarar nuevas clases?

Saludos, MAC

¿Fue útil?

Solución

Así que finalmente pude hacerlo funcionar gracias a los consejos de 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;

Esto no requiere proyección o uso de nuevas clases de extensión.

Gracias por todas sus aportaciones.

Otros consejos

Filtrado en la colección adjunta del objeto,

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

Al usar el método de extensión " Actualizar " de 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;
    }
}

Si esto proviene de una base de datos, puede ejecutar su primera declaración.

A continuación, realice una carga o inclusión de VerseTranslations con una cláusula Where.

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

¿Tiene una relación en su modelo entre Verse y VerseTranslations? En ese caso, esto podría funcionar:

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

¿No hay forma de hacerlo en tal   de manera tal que el filtrado en el   la tabla secundaria se puede incorporar en el   primera consulta linq para que no haya novedades   las clases tienen que ser declaradas?

Técnicamente, la respuesta es no. Si está intentando devolver más datos de los que puede contener un solo objeto de entidad (Verse, VerseTranslation), necesitará algún tipo de objeto para "proyectar" dentro. Sin embargo, puede moverse declarando explícitamente myType utilizando un 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);

El compilador generará una clase con las propiedades de Verse y Traducciones escritas apropiadamente para usted. Puede usar estos objetos para casi cualquier cosa siempre que no necesite hacer referencia al tipo por nombre (para regresar de un método con nombre, por ejemplo). Entonces, si bien no estás técnicamente "declarando" un tipo, todavía está utilizando un nuevo tipo que se generará según su especificación.

En cuanto a utilizar una sola consulta LINQ, todo depende de cómo desee estructurar los datos. Para mí, parece que su consulta original tiene más sentido: empareje cada Verse con una lista filtrada de traducciones. Si espera una sola traducción por idioma, puede usar SingleOrDefault (o FirstOrDefault ) para aplanar su subconsulta, o simplemente usar un SelectMany como esto:

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

Si un verso tiene múltiples traducciones, esto devolverá una " fila " para cada par verso / traducción. Uso DefaultIfEmpty () como combinación izquierda para asegurarme de que obtendremos todos los Versos, incluso si les falta una traducción.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top