如何将表达式>转换为到表达式>?

发布于 2024-11-06 08:09:49 字数 1759 浏览 2 评论 0 原文

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

谢谢, 贾森

I am trying build a dictionary of Expressions that have different input parameter types. I am trying to store the type of the parameter because later down the road I plan to use Reflection to discover a method on the type. Here is the code that creates the dictionary and a generic Add function I created to add entries to it:

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

The body of the new method is created properly. The issue is when I try to create the new Lambda expression with the type from the expression being passed in, I receive this error:

ParameterExpression of type 'TestNamespace.TestClass' cannot be used for delegate parameter of type 'System.Type'

Does anybody have an idea as to what I can do in this situation? Like I said before, at some point later I am going to loop through this dictionary to do some reflective programming on each entry. If there is a better way to do this I am all ears.

As an example of what I am trying to do, I store the expressions for Where clauses for POCO objects that need to be initialized:

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

This list of Entities will be different for each app. The idea is to collect all the entities and Where clause for each and discover a certain method using reflection on each one. (e.g. PayrollLocation has a GetPayrollLocationsQuery() method, PayrollGroupBU has a GetPayrollGroupBUQuery() method...). The Add method is generic in order for me to make use of the lambda expression in the calling code.

Thanks,
Jason

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

对你而言 2024-11-13 08:09:49

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

更大的问题是你的表达方式与你似乎想做的事情是错误的。据我所知,您只是尝试创建从实体类型到采用该类型实体并返回 bool 的函数的映射。 类型 ->表达式>。您构建的表达式不起作用。

您应该使字典存储非泛型 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));

Looking closely at your code, the expression you generate has some problems. See my explanation at the top of this answer to explain one of them, it's the same issue here. You're creating a new lambda where the parameter instance you create here is not used in the body.

The bigger problem is that your expressions are just wrong for what you appear to be trying to do. As far as I can tell, you are just trying to create a mapping from entity types to functions that take an entity of that type and returns a bool. Type -> Expression<Func<TEntity, bool>>. The expression you build just does not work.

You should make the dictionary store non-generic lambdas that way you can store these functions easily without performing conversions or rebuilding the expressions. You will not be able to store them as generic lambdas here. Then cast to the generic lambda when you access them. I'd put this in a separate class to manage the casting and refactor your code to this:

// 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));
总以为 2024-11-13 08:09:49

我认为这不会达到您期望的效果; Func 定义一个函数,该函数采用类型作为参数并返回 bool。 Func 定义一个函数,该函数将 T 类型的对象作为参数并返回 bool。字典中定义的 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)];
    }
}

I don't think this is going to do what you expect it to do; Func<Type, bool> defines a function that takes as a parameter a type and returns a bool. Func<T, bool> defines a function that takes as a parameter an object of type T and returns a bool. The lambda that is defined in your dictionary is never going to receive the object that you are trying to filter on, only its type.

To me the quickest way to make this in any way appropriate would be to make the LoadEntityQuery class generic on the type of the parameter that you expect your function to accept, but that probably will limit you in other ways...

You could use an object and cast it... Not the best solution, but at least it encapsulates the casting and is a fairly small piece, while still allowing you to do what it appears to me you need to do.

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)];
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文