¿Cómo puedo pasar una expresión Lambda a un IncludeFilter?


Pregunta

Estoy tratando de pasar un LambdaExpression generado dinámicamente a un IncludeFilter, de la siguiente manera:

EDITAR: He cambiado mi código de prueba a lo siguiente, ya que (correctamente) no estaba implementando mi declaración "Dónde". La declaración correcta donde se está generando, pero no puedo pasar la declaración lambda a la llamada 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());
        }

Esto está integrado en una clase abstracta que me permite pasar un tipo de entidad, recuperar los registros y reutilizar el método independientemente del tipo de entidad; sin embargo, también estoy intentando filtrar las entidades secundarias que están marcadas como eliminadas (por lo tanto, el uso de EF +).

¿Cómo puedo hacer esto?

EDIT 2: Entonces, me di cuenta de que también tengo Linq.Dynamic.Core (!) En mi solución, así que ya tengo acceso a analizar un LambdaExpression de una cadena. Sin embargo, el error que recibo dice que IncludeFilter no sabe qué método está tratando de usar. (Veo en el Examinador de objetos que uno usa Expresión> y otro que usa Expresión >>. Si pudiera averiguar cómo hacer que el IncludeFilter reconozca qué método, creo que ya habría terminado. Aquí hay una muestra del código I he reescrito

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

¿Hay una manera de "forzar" (por falta de un término mejor) el IncludeFilter para usar un método? ¿Se pasa un valor en lugar de un valor nulo en el analizador?

Por cierto, gracias por tu ayuda. Su biblioteca de EFP es realmente excelente.

Respuesta experta

Descargo de responsabilidad : soy el propietario del proyecto Entity Framework Plus

Sí, es posible, pero solo si puede especificar el tipo de argumento genérico requerido por el método explícitamente para el QueryFilter (como mencionó en su comentario).

De lo contrario, también deberá llamar a QueryFilter través de la expresión para hacer que todo sea genérico.


Sin embargo, su expresión actual parece tener algún error, como no llamar a los métodos Where .

Lo que quieres lograr es probablemente algo similar a esto:

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


Descargo de responsabilidad : soy el propietario del proyecto Eval-Expression.NET

Esta biblioteca no es gratuita, pero hace que trabajar con una expresión dinámica sea más fácil y rápido.

Una vez que te acostumbras, puedes crear rápidamente una expresión dinámica en solo unas pocas líneas, como normalmente escribes LINQ. Aquí hay un código que podría manejar un escenario similar a tu:

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

EDITAR: responder a la pregunta

Por favor revise mi código cambiado

Todavía te falta la cláusula where .

Lo que tienes es algo similar a esto como comentaste

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

Lo que quieres es algo similar a esto.

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

EDITAR: responder a la pregunta

Oh, lo siento, ahora entiendo el problema con eso.

Si no conoce el tipo, deberá llamar también IncludeFilter en una expresión para que todo sea genérico. No se puede llamar explícitamente como lo intentas hacer.





Licencia bajo: CC-BY-SA
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué