Как преобразовать выражение <Func <T, bool >> в выражение <Func <Type, bool >>? - PullRequest
2 голосов
/ 10 мая 2011

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

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

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

ParameterExpression типа '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 является общим для того, чтобы я использовал лямбда-выражение в вызывающем коде.

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

Ответы [ 2 ]

4 голосов
/ 11 мая 2011

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

Большая проблема в том, что ваши выражения просто не соответствуют тому, что вы пытаетесь сделать. Насколько я могу судить, вы просто пытаетесь создать отображение из типов сущностей в функции, которые принимают сущность этого типа и возвращают логическое значение. 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));
0 голосов
/ 10 мая 2011

Я не думаю, что это будет делать то, что вы ожидаете; 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)];
    }
}
...