Come posso passare LambdaExpression a IncludeFilter?


Domanda

Sto cercando di passare un LambdaExpression generato dinamicamente ad un IncludeFilter, come segue:

EDIT: Ho cambiato il mio codice di test al seguente, in quanto (correttamente) non stavo implementando la mia dichiarazione "Dove". L'istruzione where corretta viene generata, ma non posso passare l'istruzione lambda nella chiamata IncludeFilter:

        DbSet<MyTestEntity> dbSet = db.Set<MyTestEntity>();
        ParameterExpression parameter = Expression.Parameter(typeof(MyTestEntity), "t");
        Expression idProperty = Expression.Property(parameter, "mytestentityid");
        Expression delProperty = Expression.Property(parameter, "deleted");
        Expression delTarget = Expression.Constant(false, typeof(bool));
        Expression deletedMethod = Expression.Call(delProperty, "Equals", null, delTarget);
        Expression<Func<MyTestEntity, bool>> lambda = Expression.Lambda<Func<MyTestEntity, bool>>(deletedMethod, parameter);
        IQueryable<MyTestEntity> query = dbSet.Where(lambda);
        Console.WriteLine("Current Query: {0}", query.ToString());
        foreach (string include in includes)
        {
            Type subType = db.GetType().Assembly.GetTypes().SingleOrDefault(x => x.Name.EndsWith(include));
            Assert.IsNotNull(subType);
            ParameterExpression innerParam = Expression.Parameter(subType, subType.Name);
            Assert.IsNotNull(innerParam);
            MemberExpression inrDelProp = Expression.Property(innerParam, "deleted");
            Assert.IsNotNull(inrDelProp);
            ConstantExpression inrDelCstProp = Expression.Constant(false, typeof(bool));
            Assert.IsNotNull(inrDelCstProp);
            MethodCallExpression inrDelMthd = Expression.Call(inrDelProp, "Equals", null, inrDelCstProp);
            Assert.IsNotNull(inrDelMthd);
            var delegateType = typeof(Func<,>).MakeGenericType(subType, typeof(bool));
            dynamic inrLmbdaExpr = Expression.Lambda(delegateType, inrDelMthd, innerParam);
            Assert.IsNotNull(inrLmbdaExpr);
            Console.WriteLine("inrLmbdaExpr: {0}", inrLmbdaExpr.ToString()); // Result: MyTestEntityChild => MyTestEntityChild.deleted.Equals(false)
            query = query.IncludeFilter(inrLmbdaExpr); // ERROR HERE
            Assert.IsNotNull(query);
            Console.WriteLine("-------------------------------------------------");
            Console.WriteLine("Current Query: {0}", query.ToString());
        }

Questo è incorporato in una classe astratta che mi consente di passare un tipo di entità, recuperare i record e riutilizzare il metodo indipendentemente dal tipo di entità; tuttavia, sto anche cercando di filtrare le entità figlio che sono contrassegnate come cancellate (quindi l'uso di EF +).

Come posso fare questo?

EDIT 2: Quindi, ho realizzato che ho anche Linq.Dynamic.Core (!) Nella mia soluzione, quindi ho già accesso all'analisi di LambdaExpression da stringa. Tuttavia, l'errore che viene visualizzato indica che IncludeFilter non sa quale metodo sta tentando di utilizzare. (Vedo nel Visualizzatore oggetti che si usa Expression> e si usa Expression >>. Se solo riuscissi a capire come ottenere IncludeFilter per riconoscere quale metodo, penso che sarebbe stato fatto! Ecco un esempio del codice I hai riscritto:

string myIncStr = String.Format("x => x.{0}.Where(s => s.deleted.Equals(false)).Where(x => x.MyEntityId.Equals(IncomingId)",includedEntityName);
IEnumerable<MyEntity> result = db.MyEntity.IncludeFilter(System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(typeof(MyChildEntity), myIncStr, null));

C'è un modo per "forzare" (per mancanza di un termine migliore) il IncludeFilter per usare un metodo? Passa un valore anziché null nel parser?

A proposito, grazie per il vostro aiuto. La tua libreria EFP è in realtà eccellente.

Risposta esperta

Disclaimer : sono il proprietario del progetto Entity Framework Plus

Sì, è possibile ma solo se è possibile specificare il tipo di argomento generico richiesto dal metodo esplicitamente per QueryFilter (come indicato nel commento).

In caso contrario, sarà necessario chiamare anche QueryFilter tramite l'espressione per rendere tutto generico.


Tuttavia, la tua espressione corrente sembra avere qualche errore come non chiamare i metodi Where .

Quello che vuoi ottenere è probabilmente qualcosa di simile a questo:

query = query.IncludeFilter(x => x.Childs.Where(y => !y.Deleted));


Disclaimer : sono il proprietario del progetto Eval-Expression.NET

Questa libreria non è gratuita ma semplifica e velocizza il lavoro con un'espressione dinamica.

Una volta che ci si abitua, è possibile creare rapidamente un'espressione dinamica in poche righe, come si scrive normalmente LINQ. Ecco un codice che potrebbe gestire uno scenario simile al tuo:

using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Windows.Forms;
using Z.Expressions;

namespace Z.EntityFramework.Plus.Lab.EF6
{
    public partial class Form_Request_IncludeFilter_Dynamic : Form
    {
        public Form_Request_IncludeFilter_Dynamic()
        {
            InitializeComponent();

            // CLEAN
            using (var context = new EntityContext())
            {
                context.MyEntityClasses.RemoveRange(context.MyEntityClasses);
                context.MyEntityClassToFilters.RemoveRange(context.MyEntityClassToFilters);
                context.SaveChanges();
            }

            // SEED
            using (var context = new EntityContext())
            {
                var entity1 = context.MyEntityClasses.Add(new MyEntityClass {ColumnInt = 1, Childs = new List<MyEntityClassToFilter>()});
                entity1.Childs.Add(new MyEntityClassToFilter {ColumnInt = 1, Deleted = true});
                entity1.Childs.Add(new MyEntityClassToFilter {ColumnInt = 2, Deleted = false});
                context.MyEntityClasses.Add(new MyEntityClass {ColumnInt = 2});
                context.MyEntityClasses.Add(new MyEntityClass {ColumnInt = 3});
                context.SaveChanges();
            }

            // TEST
            using (var context = new EntityContext())
            {
                // You must register extension method only once
                // That should not be done here, but for example purpose
                EvalManager.DefaultContext.RegisterExtensionMethod(typeof(QueryIncludeFilterExtensions));

                // That could be also dynamic. I believe you already handle this part
                IQueryable<MyEntityClass> query = context.MyEntityClasses;

                // The path to include
                var include = "Childs";

                // The dynamic expression to execute
                var dynamicExpression = "IncludeFilter(x => x." + include + ".Where(y => !y.Deleted));";
                query = query.Execute<IQueryable<MyEntityClass>>(dynamicExpression);

                // The result
                var list = query.ToList();
            }
        }

        public class EntityContext : DbContext
        {
            public EntityContext() : base("CodeFirstEntities")
            {
            }

            public DbSet<MyEntityClass> MyEntityClasses { get; set; }
            public DbSet<MyEntityClassToFilter> MyEntityClassToFilters { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Types().Configure(x =>
                    x.ToTable(GetType().DeclaringType != null
                        ? GetType().DeclaringType.FullName.Replace(".", "_") + "_" + x.ClrType.Name
                        : ""));

                base.OnModelCreating(modelBuilder);
            }
        }

        public class MyEntityClass
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }

            public List<MyEntityClassToFilter> Childs { get; set; }
        }

        public class MyEntityClassToFilter
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }

            public bool Deleted { get; set; }
        }
    }
}

MODIFICA: rispondere alla domanda

Si prega di rivedere il mio codice modificato

Siete ancora manca il where clausola.

Quello che hai è qualcosa di simile a questo come hai commentato

// Result: MyTestEntityChild => MyTestEntityChild.deleted.Equals(false)

Quello che vuoi è qualcosa di simile a questo

// Result: MyTestEntityChild => MyTestEntityChild.Where(x => x.deleted.Equals(false))

MODIFICA: rispondere alla domanda

Oh scusa, ora capisco il problema con esso.

Se non si conosce il tipo, è necessario chiamare il IncludeFilter in un'espressione per rendere tutto generico. Non può essere definito esplicitamente come si sta tentando di fare.





Autorizzato sotto: CC-BY-SA
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché