Wie kann ich eine LambdaExpression an einen IncludeFilter übergeben?

entity-framework entity-framework-plus

Frage

Ich versuche, eine dynamisch generierte LambdaExpression wie folgt an einen IncludeFilter zu übergeben:

EDIT: Ich habe meinen Testcode in den folgenden geändert, als (richtig) ich meine "Wo" -Anweisung nicht implementiert habe. Die korrekte where-Anweisung wird generiert, aber ich kann die lambda-Anweisung nicht an den IncludeFilter-Aufruf übergeben:

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

Dies ist in eine abstrakte Klasse integriert, die es mir ermöglicht, einen Entitätstyp zu übergeben, die Datensätze abzurufen und die Methode unabhängig vom Entitätstyp wiederzuverwenden. Ich versuche jedoch auch, untergeordnete Elemente herauszufiltern, die als gelöscht markiert sind (also die Verwendung von EF +).

Wie kann ich das machen?

EDIT 2: Also, ich erkannte, dass ich auch Linq.Dynamic.Core (!) In meiner Lösung habe, also habe ich bereits Zugriff auf eine LambdaExpression von String. Der Fehler, den ich erhalte, besagt jedoch, dass IncludeFilter nicht weiß, welche Methode er verwenden möchte. (Ich sehe im Objekt-Browser, dass man Expression> verwendet und man Expression >> verwendet. Wenn ich nur herausfinden könnte, wie man den IncludeFilter dazu bringt, welche Methode zu erkennen, denke ich, dass ich fertig wäre! Hier ist ein Beispiel für den Code I habe neu geschrieben:

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

Gibt es eine Möglichkeit, den IncludeFilter zu "erzwingen" (aus Mangel an einem besseren Begriff), um eine Methode zu verwenden? Ist es im Parser ein Wert statt Null?

BTW, danke für deine Hilfe. Ihre EFP-Bibliothek ist eigentlich exzellent.

Expertenantwort

Haftungsausschluss : Ich bin der Eigentümer des Projekts Entity Framework Plus

Ja, das ist möglich, aber nur, wenn Sie den generischen Argumenttyp angeben können, der von der Methode explizit für den QueryFilter (wie Sie in Ihrem Kommentar erwähnt haben).

Andernfalls müssen Sie den QueryFilter über den Ausdruck QueryFilter , um alles generisch zu machen.


Ihr aktueller Ausdruck scheint jedoch einen Fehler zu haben, beispielsweise die Where Methoden nicht aufzurufen.

Was Sie erreichen möchten, ist wahrscheinlich etwas Ähnliches:

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


Disclaimer : Ich bin der Besitzer des Projekts Eval-Expression.NET

Diese Bibliothek ist nicht kostenlos, erleichtert aber das Arbeiten mit einem dynamischen Ausdruck.

Sobald Sie sich daran gewöhnt haben, können Sie schnell einen dynamischen Ausdruck in nur wenigen Zeilen erstellen, während Sie normalerweise LINQ schreiben. Hier ist ein Code, der mit einem ähnlichen Szenario wie Ihrem umgehen könnte:

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: Beantworten Sie die Frage

Bitte überprüfen Sie meinen geänderten Code

Sie vermissen immer noch die where Klausel.

Was Sie haben, ist etwas Ähnliches wie Sie kommentiert haben

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

Was Sie wollen, ist etwas Ähnliches

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

EDIT: Beantworten Sie die Frage

Oh Entschuldigung, ich verstehe jetzt das Problem damit.

Wenn Sie den Typ nicht kennen, müssen Sie den IncludeFilter auch in einem Ausdruck aufrufen, um alles generisch zu machen. Es kann nicht explizit so genannt werden, wie Sie es versuchen.




Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum