Wie man den Ausdruck konvertiert > zum Ausdruck >?

expression-trees functional-programming generics linq reflection

Frage

Ich versuche, ein Wörterbuch von Ausdrücken zu erstellen, die verschiedene Eingabeparametertypen haben. Ich versuche, den Typ des Parameters zu speichern, weil ich später die Straße verwenden möchte, um eine Methode für den Typ zu finden. Hier ist der Code, der das Wörterbuch erstellt und eine generische Add-Funktion, die ich erstellt habe, um ihr Einträge hinzuzufügen:

public class LoadEntityQuery : IQuery<LoadEntityQueryResult>
{
    public IDictionary<Type, Expression<Func<Type, bool>>> Entities { get; set; } 
    public LoadEntityQuery()
    {
        Entities = new Dictionary<Type, Expression<Func<Type, bool>>>();
    }

    public void Add<T>(Expression<Func<T, bool>> where = null) where T : Entity
    {
        Expression<Func<Type, bool>> _lambda = null;

        if (where != null)
        {
            ParameterExpression param = Expression.Parameter(typeof(T), where.Parameters[0].Name);

            var body = Expression.Invoke(where, param);
            _lambda = Expression.Lambda<Func<Type, bool>>(body, param);
        }

        Entities.Add(typeof(T), _lambda);
    }
}

Der Körper der neuen Methode wird ordnungsgemäß erstellt. Das Problem ist, wenn ich versuche, den neuen Lambda-Ausdruck mit dem Typ aus dem Ausdruck zu erstellen, der übergeben wird, erhalte ich diesen Fehler:

ParameterExpression des Typs 'TestNamespace.TestClass' kann nicht für Delegate-Parameter des Typs 'System.Type' verwendet werden

Hat jemand eine Idee, was ich in dieser Situation tun kann? Wie ich schon sagte, werde ich irgendwann später dieses Wörterbuch durchlaufen, um bei jedem Eintrag eine reflektierende Programmierung vorzunehmen. Wenn es einen besseren Weg gibt, dies zu tun, bin ich ganz Ohr.

Als Beispiel für das, was ich versuche, speichere ich die Ausdrücke für Where-Klauseln für POCO-Objekte, die initialisiert werden müssen:

LoadEntityQuery _query = new LoadEntityQuery();
    _query.Add<PayrollLocation>();
    _query.Add<PayrollGroupBU>();
    _query.Add<PersonnelPosition>(t => t.DataSet == MasterDataSet);
    _query.Add<EmployeeStatus>();
    _query.Add<PayrollGrade>();

Diese Liste von Entitäten wird für jede App unterschiedlich sein. Die Idee besteht darin, alle Entitäten und die Where-Klausel für jeden zu sammeln und eine bestimmte Methode zu finden, die die Reflexion auf jeder einzelnen Methode verwendet. (zB PayrollLocation hat eine GetPayrollLocationsQuery () Methode, PayrollGroupBU hat eine GetPayrollGroupBUQuery () Methode ...). Die Add-Methode ist generisch, damit ich den Lambda-Ausdruck im aufrufenden Code verwenden kann.

Danke, Jason

Akzeptierte Antwort

Wenn Sie Ihren Code genau betrachten, hat der von Ihnen erzeugte Ausdruck einige Probleme. Siehe meine Erklärung oben auf dieser Antwort , um eine davon zu erklären, es ist das gleiche Problem hier. Sie erstellen ein neues Lambda, bei dem die hier erstellte Parameterinstanz nicht im Rumpf verwendet wird.

Das größere Problem ist, dass Ihre Ausdrücke einfach falsch sind für das, was Sie scheinbar zu tun versuchen. Soweit ich das beurteilen kann, versuchen Sie nur, eine Zuordnung von Entitätstypen zu Funktionen zu erstellen, die eine Entität dieses Typs annehmen und einen Bool zurückgeben. Type -> Expression<Func<TEntity, bool>> . Der Ausdruck, den Sie erstellen, funktioniert einfach nicht.

Sie sollten den Dictionary-Store zu nicht generischen Lambdas machen, damit Sie diese Funktionen einfach speichern können, ohne Konvertierungen vorzunehmen oder die Ausdrücke neu zu erstellen. Sie können sie hier nicht als generische Lambdas speichern. Wenn Sie auf sie zugreifen, werden Sie in das generische Lambda umgewandelt. Ich würde dies in eine separate Klasse legen, um das Casting zu verwalten und deinen Code zu diesem zu refaktorisieren:

// add all necessary error checking where needed and methods
public class EntityPredicateDictionary
{
    private Dictionary<Type, LambdaExpression> dict = new Dictionary<Type, LambdaExpression>();

    public Expression<Func<TEntity, bool>> Predicate<TEntity>() where TEntity : Entity
    {
        return (Expression<Func<TEntity, bool>>)dict[typeof(TEntity)];
    }

    public LambdaExpression Predicate(Type entityType)
    {
        return dict[entityType];
    }

    internal void Add<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : Entity
    {
        dict.Add(typeof(TEntity), predicate);
    }
}

public class LoadEntityQuery : IQuery<LoadEntityQueryResult>
{
    public EntityPredicateDictionary Entities { get; private set; }
    public LoadEntityQuery()
    {
        Entities = new EntityPredicateDictionary();
    }

    public void Add<TEntity>(Expression<Func<TEntity, bool>> predicate = null) where TEntity : Entity
    {
        Entities.Add(predicate);
    }
}

// then to access the predicates
LoadEntityQuery query = ...;
var pred1 = query.Entities.Predicate<Entity1>();
var pred2 = query.Entities.Predicate(typeof(Entity2));

Beliebte Antwort

Ich glaube nicht, dass das tun wird, was Sie erwarten; Func<Type, bool> definiert eine Funktion, die als Parameter einen Typ annimmt und ein Bool zurückgibt. Func<T, bool> definiert eine Funktion, die als Parameter ein Objekt vom Typ T annimmt und ein bool zurückgibt. Das Lambda, das in Ihrem Wörterbuch definiert ist, wird nie das Objekt erhalten, nach dem Sie filtern möchten, sondern nur seinen Typ.

Für mich wäre der schnellste Weg, dies in geeigneter Weise zu tun, die LoadEntityQuery-Klasse für den Typ des Parameters generisch zu machen, von dem Sie erwarten, dass Ihre Funktion dies akzeptiert, aber das wird Sie wahrscheinlich auf andere Weise einschränken ...

Du könntest ein Objekt benutzen und es werfen ... Nicht die beste Lösung, aber zumindest kapselt es das Casting ein und ist ein ziemlich kleines Stück, während du immer noch tun kannst, was du mir vorstellst.

public class LoadEntityQuery : IQuery<LoadEntityQueryResult>
{
    // Note: Hide this, we don't want anyone else to have to think about the cast.
    private IDictionary<Type, object> Entities { get; set; }

    public void Add<T>(Expression<Func<T, bool>> where = null) where T : Entity
    {
        Entities.Add(typeof(T), where);
    }

    public Expression<Func<T, bool>> Retrieve<T>() where T : Entity
    {
        if (!Entities.ContainsKey(typeof(T)))
            return null;
        return (Expression<Func<T, bool>>)Entities[typeof(T)];
    }
}


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