Comment convertir Expression > à l'expression >?

expression-trees functional-programming generics linq reflection

Question

J'essaie de créer un dictionnaire d'expressions comportant différents types de paramètres d'entrée. J'essaie de stocker le type du paramètre car, plus tard, je compte utiliser Reflection pour découvrir une méthode sur ce type. Voici le code qui crée le dictionnaire et une fonction générique Add que j'ai créée pour y ajouter des entrées:

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

Le corps de la nouvelle méthode est créé correctement. Le problème est que lorsque j'essaie de créer la nouvelle expression Lambda avec le type de l'expression transmise, je reçois cette erreur:

ParameterExpression de type 'TestNamespace.TestClass' ne peut pas être utilisé pour le paramètre de délégué de type 'System.Type'

Quelqu'un a-t-il une idée de ce que je peux faire dans cette situation? Comme je l'ai déjà dit, un peu plus tard, je vais parcourir ce dictionnaire pour faire de la programmation réflexive sur chaque entrée. S'il y a un meilleur moyen de faire cela, je suis tout ouïe.

À titre d'exemple de ce que j'essaie de faire, je stocke les expressions des clauses Where pour les objets POCO qui doivent être initialisés:

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

Cette liste d'entités sera différente pour chaque application. L'idée est de collecter toutes les entités et la clause Where pour chacune et de découvrir une méthode en utilisant la réflexion sur chacune d'elles. (Par exemple, PayrollLocation a une méthode GetPayrollLocationsQuery (), PayrollGroupBU a une méthode GetPayrollGroupBUQuery () ...). La méthode Add est générique pour que je puisse utiliser l'expression lambda dans le code appelant.

Merci Jason

Réponse acceptée

En regardant de près votre code, l'expression que vous générez présente des problèmes. Voir mon explication en haut de cette réponse pour expliquer l'une d'entre elles, c'est le même problème ici. Vous créez un nouveau lambda où l'instance de paramètre que vous créez ici n'est pas utilisée dans le corps.

Le plus gros problème est que vos expressions sont tout simplement fausses pour ce que vous semblez vouloir faire. Autant que je sache, vous essayez simplement de créer un mappage de types d'entité à des fonctions qui prennent une entité de ce type et renvoie une valeur booléenne. Type -> Expression<Func<TEntity, bool>> . L'expression que vous construisez ne fonctionne tout simplement pas.

Vous devriez faire en sorte que le dictionnaire stocke les lambdas non génériques de cette façon, vous pouvez facilement stocker ces fonctions sans effectuer de conversions ni reconstruire les expressions. Vous ne pourrez pas les stocker ici sous forme de lambdas génériques. Puis lancez le générique lambda lorsque vous y accédez. Je mettrais ceci dans une classe séparée pour gérer le casting et refactoriser votre code en ceci:

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

Réponse populaire

Je ne pense pas que cela va faire ce que vous attendez de faire; Func<Type, bool> définit une fonction qui prend en paramètre un type et retourne un bool. Func<T, bool> définit une fonction qui prend en paramètre un objet de type T et retourne un bool. Le lambda défini dans votre dictionnaire ne recevra jamais l'objet sur lequel vous essayez de filtrer, seul son type.

Pour moi, le moyen le plus rapide de rendre cela approprié, de quelque manière que ce soit, serait de rendre la classe LoadEntityQuery générique sur le type du paramètre que vous attendez de votre fonction, mais cela vous limitera probablement d'une autre manière ...

Vous pouvez utiliser un objet et le mouler ... Ce n'est pas la meilleure solution, mais au moins, il encapsule le casting et constitue un morceau relativement petit, tout en vous permettant de faire ce que vous semblez devoir faire.

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


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow