Cómo convertir la expresión > a la expresión >?

expression-trees functional-programming generics linq reflection

Pregunta

Estoy tratando de construir un diccionario de Expresiones que tengan diferentes tipos de parámetros de entrada. Estoy tratando de almacenar el tipo de parámetro porque más adelante en el futuro planeo usar Reflection para descubrir un método en el tipo. Aquí está el código que crea el diccionario y una función Agregar genérica que creé para agregarle entradas:

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

El cuerpo del nuevo método se crea correctamente. El problema es cuando intento crear la nueva expresión Lambda con el tipo de la expresión que se pasa, recibo este error:

ParameterExpression de tipo 'TestNamespace.TestClass' no se puede usar para el parámetro delegado de tipo 'System.Type'

¿Alguien tiene una idea de lo que puedo hacer en esta situación? Como dije antes, en algún momento más adelante voy a recorrer este diccionario para hacer una programación reflexiva en cada entrada. Si hay una mejor manera de hacerlo, soy todo oídos.

Como ejemplo de lo que estoy tratando de hacer, almaceno las expresiones de las cláusulas Where para los objetos POCO que deben inicializarse:

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

Esta lista de Entidades será diferente para cada aplicación. La idea es recopilar todas las entidades y la cláusula Where para cada una y descubrir un método determinado mediante la reflexión en cada una. (Por ejemplo, PayrollLocation tiene un método GetPayrollLocationsQuery (), PayrollGroupBU tiene un método GetPayrollGroupBUQuery () ...). El método Agregar es genérico para que pueda utilizar la expresión lambda en el código de llamada.

Gracias jason

Respuesta aceptada

Mirando de cerca su código, la expresión que genera tiene algunos problemas. Vea mi explicación en la parte superior de esta respuesta para explicar uno de ellos, es el mismo problema aquí. Estás creando un nuevo lambda donde la instancia del parámetro que creas aquí no se usa en el cuerpo.

El mayor problema es que tus expresiones son incorrectas por lo que pareces estar intentando hacer. Por lo que puedo decir, solo está intentando crear una asignación de tipos de entidad a funciones que toman una entidad de ese tipo y devuelven un bool. Type -> Expression<Func<TEntity, bool>> . La expresión que construyes simplemente no funciona.

Debe hacer que el diccionario almacene lambdas no genéricas de manera que pueda almacenar estas funciones fácilmente sin realizar conversiones ni reconstruir las expresiones. No podrás almacenarlas como lambdas genéricas aquí. Luego lanza al lambda genérico cuando accedas a ellos. Pondría esto en una clase separada para administrar el casting y refactorizar su código a esto:

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

Respuesta popular

No creo que esto vaya a hacer lo que esperas que haga; Func<Type, bool> define una función que toma como parámetro un tipo y devuelve un bool. Func<T, bool> define una función que toma como parámetro un objeto de tipo T y devuelve un bool. La lambda que está definida en su diccionario nunca recibirá el objeto que está tratando de filtrar, solo su tipo.

Para mí, la forma más rápida de hacer esto de una manera apropiada sería hacer que la clase LoadEntityQuery sea genérica en el tipo de parámetro que espera que su función acepte, pero que probablemente lo limitará de otras maneras ...

Podría usar un objeto y lanzarlo ... No es la mejor solución, pero al menos encapsula el lanzamiento y es una pieza bastante pequeña, mientras que aún le permite hacer lo que me parece que debe hacer.

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


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