【问题标题】:How to convert Expression<Func<T, bool>> to Expression<Func<Type, bool>>?如何将 Expression<Func<T, bool>> 转换为 Expression<Func<Type, bool>>?
【发布时间】:2011-05-10 12:27:35
【问题描述】:

我正在尝试构建具有不同输入参数类型的表达式字典。我正在尝试存储参数的类型,因为稍后我计划使用反射来发现该类型的方法。这是创建字典的代码和我创建的用于向其中添加条目的通用 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);
    }
}

新方法的主体已正确创建。问题是当我尝试使用传入的表达式的类型创建新的 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 表达式。

谢谢, 杰森

【问题讨论】:

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


    【解决方案1】:

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

    更大的问题是,你的表达方式与你试图做的事情不符。据我所知,您只是试图创建从实体类型到采用该类型实体并返回布尔值的函数的映射。 Type -&gt; Expression&lt;Func&lt;TEntity, bool&gt;&gt;。您构建的表达式不起作用。

    您应该让字典存储非泛型 lambda,这样您就可以轻松存储这些函数,而无需执行转换或重建表达式。您将无法在此处将它们存储为通用 lambda。然后在您访问它们时转换为通用 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));
    

    【讨论】:

    • 完美!谢谢杰夫 - 这是一个真正的帮助(就像你的其他帖子一样)。我有很多关于 LambdaExpressions 的知识,我对它们还是很陌生。谢谢你的帮助。
    【解决方案2】:

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

    对我来说,以任何方式实现这一点的最快方法是使 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)];
        }
    }
    

    【讨论】:

    • 我最初有这个设计 - 它在某些方面受到限制。
    • @Rockdocta - 我添加了一个替代解决方案,也许它至少会给你一个尝试的想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 2023-03-31
    相关资源
    最近更新 更多