Búsqueda deseable en lista anidada por lista anidada

Por favor, alguien me puede ayudar debajo de Linq EF Tengo objetos similares

Class Product 
{
    public string Name { get; set; }       
    public virtual ICollection ProductAttributes { get; set; }
}
class sc : Product { }

class ProductAttributes 
{
    public long ProductID { get; set; }
    public virtual Product Product { get; set; }
    public long AttributesDtlID { get; set; }
}

cuando estoy tratando de buscar en el producto = ProductoAtributos con SC = confianza ProductoAtributos Había creado abajo Queryable

Product.Where(X => X.ProductAttributes.Any(m => sc.ProductAttributes.Any(A => m.AttributesDtlID == A.AttributesDtlID))); 

También lo intenté

Product.Where(X => X.ProductAttributes.AsEnumerable().Any(m => sc.ProductAttributes.Any(A => m.AttributesDtlID == A.AttributesDtlID))); 

pero tengo el error de abajo

LINQ expression ''DbSet
.Where(p => p.IsDeleted == False)
.Where(p => (MaterializeCollectionNavigation(
    navigation: Navigation: Product.ProductAttributes,
    subquery: DbSet
        .Where(p0 => EF.Property>(p, "ID") != null && EF.Property>(p, "ID") == EF.Property>(p0, "ProductID")))
    .AsEnumerable()
    .Any(m => __sc_ProductAttributes_0
        .Any(A => m.AttributesDtlID == A.AttributesDtlID)))'' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

Pregunta hecha hace 3 años, 4 meses, 28 días - Por compilerchieftain


4 Respuestas:

  • class Product 
    {
        // primary key
        public long Id { get; set; }
    
        // name of product
        public string Name { get; set; }
    
        // relation to attributes      
        public virtual List ProductAttributes { get; set; }
    }
    
    class ProductAttribute
    {
        // primary key
        public long Id { get; set; }
    
        // product link
        public long ProductID { get; set; }
        public virtual Product Product { get; set; }
    
        // this is the property which you want to test in your query (as far as I have understood)
        public long AttributesDtlID { get; set; }
    }
    

    Si desea obtener todos los productos ahora que tienen cualquier vínculo con un producto con atributo id '1230' haga lo siguiente:

    DbSet Products; <-- this comes from the Entity Framework
    long id = 1230;
    List result;
    
    result = Products
        .Where(p => p.ProductAttributes
            .Where(attr => attr.AttributesDtlID == id)
            .Count != 0)
        .ToList();
    

    Si usted quiere lo mismo para una lista dada de los ids lo hace así:

    DbSet Products; <-- this comes from the Entity Framework
    List ids = new List(){1230, 100, 20}
    List result;
    
    result = Products
        .Where(p => p.ProductAttributes
            .Where(attr =>  ids.Contains(attr.AttributesDtlID))
            .Count != 0)
        .ToList();
    

    Respondida el Dec 18, 2020 a las 16:50 - por algorithmwizard

    Votos positivos: 0 | Votos negativos: 0

  • Tus clases no tienen campos de identificación y no están seguras para qué clase es.

    Pero, asumiendo:

    Class Product 
    {
        public long ID { get; set; }      <- PK
        public string Name { get; set; }        
        public virtual ICollection ProductAttributes { get; set; }
    }
    
    class ProductAttributes 
    {
        public long ID { get; set; }    <- PK
        public long ProductID { get; set; }
        public virtual Product Product { get; set; }
        public long AttributesDtlID { get; set; }
    }
    

    y quieres emular (de nuevo adivinando que la unión debe ser P.ID = A.ProductID):

    select * from Product P join ProductAttributes A on P.ID = A.ProductID where A.AttributesDtlID in (1,2,3)
    

    luego utilizar:

    List attrDtlIdsFilter = new List { 1, 2, 3 };
    List products =
       Product.Where(p => 
           ProductAttributes.Where(pa => attrDtlIdsFilter.Contains(pa.AttributesDtlID))
                            .Select(pa => pa.ProductID)
                            .Contains(p.ID));
    

    UPDATE

    Desglose de la consulta:

    // Get a List of ProductAttributes where AttrobutesDtlID is in the List
    // of attributesIds that we want to filter on
    ProductAttributes.Where(pa => attrDtlIdsFilter.Contains(pa.AttributesDtlID))
    
    // From that List, just 'select' ProductID from each ProductAttribute
    .Select(pa => pa.ProductID)
    
    // Looks to see if Product.ID is that List of ProductIDs
    .Contains(p.ID)
    

    Respondida el Dec 18, 2020 a las 16:57 - por binarybuilder

    Votos positivos: 0 | Votos negativos: 0

  • El error que estás obteniendo se debe a que estás intentando realizar una comparación entre colecciones directamente en la consulta de LINQ, lo cual no es soportado por EF. Para solucionar este problema, puedes llevar a cabo la comparación entre las colecciones después de haber obtenido los datos de la base de datos. Para ello, deberías ajustar tu consulta de la siguiente manera:

    csharp
    var productAttributesIds = sc.ProductAttributes.Select(A => A.AttributesDtlID).ToList();
    
    var result = Product
        .Where(X => X.ProductAttributes
            .Any(m => productAttributesIds.Contains(m.AttributesDtlID)))
        .ToList();
    

    En este código, primero obtienes los IDs de los atributos del producto sc y luego los utilizas para verificar si algún ProductAttributes del producto Product tiene un ID que esté contenido en la lista de IDs recién obtenida.

    Este enfoque debería evitar el error que estás experimentando y te permitirá obtener los resultados deseados.

    Respondida el Dec 19, 2020 a las 16:51 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Opción 1: Uso de AsQueryable()

    Modifica tu consulta para incluir AsQueryable() antes de llamar a Any():

    C

    Product.Where(X => X.Productczuk.AsQueryable().Any(m => sc.Productczuk.Any(A => m.czukDtlID == A.czukDtlID)))

    Opción 2: Uso de ToList()

    Convierte las colecciones internas a listas antes de llamar a Any():

    C

    Product.Where(X => X.Productzuk.ToList().Any(m => sc.Productzuk.ToList().Any(A => m.zukDtlID == A.zukDtlID)))

    Opción 3: Uso de variables intermedias

    Asigna las colecciones internas a variables intermedias y luego usa esas variables en la consulta Any():

    C

    var productczuk = X.Productczuk.ToList();

    var scProductczuk = sc.Productczuk.ToList(); Product.Where(X => productczuk.Any(m => scProductczuk.Any(A => m.czukDtlID == A.czukDtlID)))

    Explicación

    El error se produce porque la consulta contiene una subconsulta anidada que intenta evaluar la colección interna Product zuk como un conjunto de datos completo. Para evitar esto, debemos asegurarnos de que la colección interna se evalúe como un objeto de consulta enumerable (IQueryable) o como una lista (List).

    Las opciones anteriores logran esto de manera diferente:

    AsQueryable() devuelve un objeto de consulta enumerable que puede ser procesado por el proveedor de datos. ToList() convierte la colección en una lista que se evalúa localmente. * El uso de variables intermedias permite especificar explícitamente que la colección interna se evalúe localmente.

    Respondida el Dec 19, 2020 a las 16:57 - por Gemini

    Votos positivos: 0 | Votos negativos: 0