Как преобразовать выражение > Выражение >?

expression-trees functional-programming generics linq reflection

Вопрос

Я пытаюсь создать словарь выражений, которые имеют разные типы входных параметров. Я пытаюсь сохранить тип параметра, потому что позже по дороге я планирую использовать Reflection, чтобы обнаружить метод этого типа. Вот код, который создает словарь и общую функцию добавления, созданную мной для добавления в нее записей:

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

Тело нового метода создается правильно. Проблема в том, что когда я пытаюсь создать новое выражение Lambda с типом из передаваемого выражения, я получаю эту ошибку:

Параметр Выражение типа «TestNamespace.TestClass» не может использоваться для параметра делегирования типа «System.Type»

Кто-нибудь знает, что я могу сделать в этой ситуации? Как я уже говорил, в какой-то момент я собираюсь пройти через этот словарь, чтобы сделать какое-то рефлексивное программирование для каждой записи. Если есть лучший способ сделать это, я все уши.

В качестве примера того, что я пытаюсь сделать, я сохраняю выражения для предложений Where для объектов POCO, которые необходимо инициализировать:

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

Этот список объектов будет отличаться для каждого приложения. Идея состоит в том, чтобы собрать все сущности и предложение Where для каждого и обнаружить определенный метод, используя отражение на каждом из них. (например, PayrollLocation имеет метод GetPayrollLocationsQuery (), PayrollGroupBU имеет метод GetPayrollGroupBUQuery () ...). Метод Add является общим для того, чтобы я мог использовать выражение лямбда в вызывающем коде.

Спасибо, Джейсон

Принятый ответ

При взгляде на ваш код выражение, которое вы создаете, имеет некоторые проблемы. См. Мое объяснение в верхней части этого ответа, чтобы объяснить один из них, это та же проблема. Вы создаете новую lambda, где экземпляр параметра, который вы создаете здесь, не используется в теле.

Большая проблема заключается в том, что ваши выражения просто неправильны для того, что вы пытаетесь сделать. Насколько я могу судить, вы просто пытаетесь создать сопоставление от типов сущностей к функциям, которые берут сущность этого типа и возвращают bool. Type -> Expression<Func<TEntity, bool>> . Выражение, которое вы создаете, просто не работает.

Вы должны сделать словарь хранить не-общие лямбды таким образом, чтобы вы могли легко хранить эти функции, не выполняя преобразований или перестраивая выражения. Вы не сможете хранить их в качестве общих лямбдов здесь. Затем бросайте в общую лямбду, когда вы обращаетесь к ним. Я бы поместил это в отдельный класс, чтобы управлять литьем и реорганизовать ваш код:

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

Популярные ответы

Я не думаю, что это будет делать то, что вы ожидаете от этого; Func<Type, bool> определяет функцию, которая принимает в качестве параметра тип и возвращает bool. Func<T, bool> определяет функцию, которая принимает в качестве параметра объект типа T и возвращает bool. Лямбда, которая определена в вашем словаре, никогда не получит объект, который вы пытаетесь фильтровать, только его тип.

Для меня самым быстрым способом сделать это в любом случае было бы целесообразно сделать класс LoadEntityQuery обобщенным по типу параметра, который вы ожидаете от своей функции, но это, вероятно, ограничит вас другими способами ...

Вы можете использовать объект и бросить его ... Не лучшее решение, но, по крайней мере, оно инкапсулирует кастинг и представляет собой довольно небольшую часть, но все же позволяет вам делать то, что мне кажется, что вам нужно делать.

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


Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow