Search code examples
c#linqentity-framework-6predicatebuilderlinqkit

Use PredicateBuilder in SelectMany LINQ to Entity Framework


It's the first time I write here on stackoverflow.

I would like to use PredicateBuilder in a LINQ query with SelectMany.

I enter my code

public async Task<List<Articolo>> OttieniElencoArticoliFiltratoComplessoAsync
    (ExpressionStarter<Articolo> predicateArticolo,
        ExpressionStarter<ArticoloFornitore> predicateFornitore,
        ExpressionStarter<Categoria> predicateCategoria,
        ExpressionStarter<Magazzino> predicateMagazzino,
        ExpressionStarter<PrezzoVendita> predicatePrezzoVendita)
    {
        using (var database = DatabaseContext.NuovoDatabase())
        {
            var query = database.Articoli.AsExpandable();

            if (predicateArticolo != null)
            {
                query = query.Where(predicateArticolo);
            }

            if (predicateFornitore != null)
            {
                query = query.AsExpandable().SelectMany(a => a.ElencoArticoliFornitore.Where(predicateFornitore)).Select(fornitore => fornitore.Articolo);
            }

            if (predicateMagazzino != null)
            {
                query = query.AsExpandable()
                    .SelectMany(articolo => articolo.ElencoMagazzino.Where(predicateMagazzino))
                    .Select(magazzino => magazzino.Articolo);
            }

            if (predicatePrezzoVendita != null)
            {
                query = query.AsExpandable().SelectMany(articolo =>
                    articolo.ElencoPrezzoVendita.Where(predicatePrezzoVendita).Select(vendita => vendita.Articolo));
            }

            if (predicateCategoria != null)
            {
                query = query.AsExpandable()
                    .SelectMany(articolo => articolo.ElencoCategorie.Where(predicateCategoria))
                    .Select(categoria => categoria.Articolo);
            }

            return await query.ToListAsync();
        }
    }

I create the predicate like this

        private ExpressionStarter<ArticoloFornitore> PredicateFornitore()
    {
        var ritornaNull = true;
        var predicate = PredicateBuilder.New<ArticoloFornitore>();
        if (IsEnabledFiltroFornitore && FornitoreSelezionato != null)
        {
            ritornaNull = false;
            predicate = predicate.And(fornitore => fornitore.IdFornitore == FornitoreSelezionato.IdFornitore);
        }
        return ritornaNull ? null : predicate;
    }

    private ExpressionStarter<Categoria> PredicateCategoria()
    {
        var ritornaNull = true;
        var predicate = PredicateBuilder.New<Categoria>();
        if (IsEnabledCategoriaLivello1 && CategoriaLivello1Selezionata != null)
        {
            ritornaNull = false;
            predicate = predicate.And(categoria =>
                categoria.IdCategoriaLv1 == CategoriaLivello1Selezionata.IdCategoriaLv1);
        }

        if (IsEnabledCategoriaLivello2 && CategoriaLivello2Selezionata != null)
        {
            ritornaNull = false;
            predicate = predicate.And(categoria =>
                categoria.IdCategoriaLv2 == CategoriaLivello2Selezionata.IdCategoriaLv2);
        }

        if (IsEnabledCategoriaLivello3 && CategoriaLivello3Selezionata != null)
        {
            ritornaNull = false;
            predicate = predicate.And(categoria =>
                categoria.IdCategoriaLv3 == CategoriaLivello3Selezionata.IdCategoriaLv3);
        }

        if (IsEnabledCategoriaLivello4 && CategoriaLivello4Selezionata != null)
        {
            ritornaNull = false;
            predicate = predicate.And(categoria =>
                categoria.IdCategoriaLv4 == CategoriaLivello4Selezionata.IdCategoriaLv4);
        }

        if (IsEnabledCategoriaLivello5 && CategoriaLivello5Selezionata != null)
        {
            ritornaNull = false;
            predicate = predicate.And(categoria =>
                categoria.IdCategoriaLv5 == CategoriaLivello5Selezionata.IdCategoriaLv5);
        }
        return ritornaNull ? null : predicate;
    }

I tried this method on LINQPAD with fixed data instead of the PredicateBuilder and the query works. But with the PredicateBuilder I get the .NET Framework Data Provider error 1025.

How can I solve? I would like to be able to create a dynamic query that takes the parameters from the interface and returns the results.

I hope you can help me.


Solution

  • EntityFramework requires your predicates to be Expression<Func<T, bool>> rather than Func<T, bool>. This is what IQueryable.ToExpandable() does. However, your Entity objects such as articolo.ElencoPrezzoVendita are virtual ICollection objects, so the predicate is reduced to Func<T, bool> which is incompatible with EF.

    It doesn't exactly roll off the tongue but you could try something like this (I didn't know the structure of your objects) with database to ensure the Expression is used.

    if (predicatePrezzoVendita != null)
    {
        query = query.AsExpandable()
            .SelectMany(articolo =>
                database.ElencoPrezzoVendita
                    .Where(x => articolo.ForeignKeyID == x.ID)
                    .AsExpandable()
                    .Where(predicatePrezzoVendita)
                    .Select(vendita => vendita.Articolo));
    }