如何转换Expression <func <t,bool =“”>&gt;表达式<func <type,bool =“”>&gt;?</func <type,> </func <t,>

时间:2011-05-10 12:27:35

标签: linq generics reflection functional-programming expression-trees

我正在尝试构建具有不同输入参数类型的表达式字典。我试图存储参数的类型,因为后来我计划使用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'的ParameterExpression不能用于'System.Type'类型的委托参数

有没有人知道在这种情况下我能做些什么?就像我之前说的那样,稍后我会循环阅读这本词典,对每个条目进行一些反思性编程。如果有更好的方法可以做到这一点,我全心全意。

作为我想要做的一个例子,我存储了需要初始化的POCO对象的Where子句的表达式:

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表达式。

谢谢, 杰森

2 个答案:

答案 0 :(得分:4)

仔细查看代码,您生成的表达式存在一些问题。请参阅this answer顶部的解释来解释其中一个问题,这是同一个问题。您正在创建一个新的lambda,其中您在此处创建的参数实例未在正文中使用。

更大的问题是你的表达对于你似乎想要做的事情是错误的。据我所知,你只是想创建一个从实体类型到带有该类型实体并返回bool的函数的映射。 Type -> Expression<Func<TEntity, bool>>。你构建的表达式不起作用。

您应该使字典存储非通用lambda,这样您就可以轻松存储这些函数,而无需执行转换或重建表达式。您将无法将它们存储为通用lambdas。然后在访问它时转换为通用lambda。我将它放在一个单独的类中来管理转换并将代码重构为:

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

答案 1 :(得分:0)

我认为这不会像你期望的那样做; Func<Type, bool>定义了一个函数,它将一个类型作为参数并返回一个bool。 Func<T, bool>定义了一个函数,该函数将类型为T的对象作为参数并返回bool。字典中定义的lambda永远不会接收您尝试过滤的对象,只接收它的类型。

对我而言,以最合适的方式实现这一点的最快方法是使LoadEntityQuery类对您希望函数接受的参数类型具有通用性,但这可能会以其他方式限制您... < / p>

你可以使用一个物体并施放它......不是最好的解决方案,但至少它封装了铸件并且是一个相当小的部分,同时仍然允许你做你需要做的事情。< / p>

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