Comment puis-je passer un LambdaExpression à un IncludeFilter?


Question

J'essaie de transmettre une LambdaExpression générée dynamiquement à un fichier IncludeFilter, comme suit:

EDIT: J'ai changé mon code de test comme suit, car (correctement) je n'implémentais pas mon instruction "Where". L'instruction where correcte est générée, mais je ne peux pas transmettre l'instruction lambda à l'appel 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());
        }

Ceci est intégré dans une classe abstraite me permettant de passer dans un type d'entité, de récupérer les enregistrements et de réutiliser la méthode quel que soit le type d'entité; Cependant, j'essaie également de filtrer les entités enfants marquées comme supprimées (d'où l'utilisation de EF +).

Comment puis-je faire ceci?

EDIT 2: Alors, j’ai réalisé que j’avais aussi Linq.Dynamic.Core (!) Dans ma solution, alors j’ai déjà accès à l’analyse d’un LambdaExpression à partir de string. Cependant, l'erreur que je reçois indique qu'InclusFilter ne sait pas quelle méthode il essaie d'utiliser. (Je vois dans le navigateur d'objets que l'on utilise Expression> et que l'on utilise Expression >>. Si je pouvais comprendre comment obtenir que IncludeFilter identifie la méthode, je pense que cela serait fait! Voici un exemple du code I 'ai réécrit:

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

Existe-t-il un moyen de "forcer" (faute d'un meilleur terme) le filtre Include à utiliser une seule méthode? Est-ce en passant une valeur au lieu de null dans l'analyseur?

BTW, merci pour votre aide. Votre bibliothèque EFP est réellement excellente.

Réponse d'expert

Disclaimer : Je suis propriétaire du projet Entity Framework Plus

Oui, c'est possible, mais seulement si vous pouvez spécifier explicitement le type d'argument générique requis par la méthode pour QueryFilter (comme vous l'avez mentionné dans votre commentaire).

Sinon, vous devrez également appeler QueryFilter via l'expression pour que tout soit générique.


Cependant, votre expression actuelle semble avoir une erreur, telle que ne pas appeler les méthodes Where .

Ce que vous voulez réaliser ressemble probablement à ceci:

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


Disclaimer : Je suis le propriétaire du projet Eval-Expression.NET

Cette bibliothèque n'est pas gratuite mais permet de travailler avec une expression dynamique plus facilement et plus rapidement.

Une fois que vous vous êtes habitué, vous pouvez rapidement créer une expression dynamique en seulement quelques lignes en écrivant normalement LINQ. Voici un code qui pourrait gérer un scénario similaire à votre:

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

EDIT: répondre à la question

S'il vous plaît examiner mon code modifié

Vous manquez toujours la clause where .

Qu'est-ce que vous avez est quelque chose de similaire à cela que vous avez commenté

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

Ce que vous voulez, c'est quelque chose de semblable à ceci

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

EDIT: répondre à la question

Oh désolé, je comprends maintenant le problème avec ça.

Si vous ne connaissez pas le type, vous devrez également appeler IncludeFilter dans une expression pour que tout soit générique. Cela ne peut pas être appelé explicitement comme vous essayez de le faire.





Sous licence: CC-BY-SA
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi