使用 Linq.Expression 访问具有动态 lambda 的嵌套属性

Posted

技术标签:

【中文标题】使用 Linq.Expression 访问具有动态 lambda 的嵌套属性【英文标题】:Access nested properties with dynamic lambda using Linq.Expression 【发布时间】:2010-12-13 01:00:17 【问题描述】:

假设我有两个类:

class person

    int ID
    string name
    Address address

class address

    int ID
    string street
    string country

这些类或多或少是给定的,老实说,它们是通过 nHibernate 映射的 :)

在一个网格(datatables.net 作为基础)中,我想要一个与类型无关的排序。

因此我创建了一个 lambda 表达式:

  var param = Expression.Parameter(typeof(T), typeof(T).Name);
  var sortExpression = Expression.Lambda<Func<T, object>>
                              (Expression.Convert(Expression.Property(param, "Property to sort"), typeof(object)), param);

如果我将 Person 作为类型 T 传递并将“要排序的属性”替换为“名称”,它可以正常工作(创建一个正确的 lambda)。如果要排序的属性是“address.street”,它将不起作用,向我抛出以下错误:

Property 'address.street' is not defined for type 'person'

到目前为止,我只看到一个解决方案,但还不够清楚...我会尝试拆分包含 Property-Name 的字符串(由 . 拆分。)

谁能给出更好的解决方案?我需要将 sortExpression 添加到 IQueryable 对象 query.OrderBy(sortExpression)

不确定我的标题是否清楚,请继续更正。

提前致谢。

【问题讨论】:

【参考方案1】:

什么不清楚?

你必须拆分它然后使用:

Expression.Property(Expression.Property(param, "address"), "street")

【讨论】:

嗯,如果有类似 person.company.address.street 的东西,你会怎么做?我只是不知道如何“嵌套”它..或者是否有其他方法,比如 Craig 发布的...... Expression.Property(Expression.Property(Expression.Property(Expression.Property(param, "person"), "company"),"address"),"street") 简单的循环将起作用。 【参考方案2】:

这是 LukLed 答案的更通用版本:

    protected MemberExpression NestedExpressionProperty(Expression expression, string propertyName)
    
        string[] parts = propertyName.Split('.');
        int partsL = parts.Length;

        return (partsL > 1) 
            ? 
            Expression.Property( 
                NestedExpressionProperty(
                    expression, 
                    parts.Take(partsL - 1)
                        .Aggregate((a, i) => a + "." + i)
                ), 
                parts[partsL - 1]) 
            :
            Expression.Property(expression, propertyName);
    

你可以这样使用它:

var paramExpression = Expression.Parameter(this.type, "val");
var firstProp = NestedExpressionProperty(paramExpression,"address.street");

【讨论】:

这是解决此问题的一种出色且可重用的方法。谢谢。 优秀答案+1。您可以在一个班轮中做到这一点:return propertyName.Split('.').Aggregate(expression, (body, member) =&gt; Expression.PropertyOrField(body, member));. 如果你不喜欢花哨的衬里,也许更易读的版本是使用foreach,例如:Expression body = expression; foreach (var member in propertyName.Split('.')) body = Expression.PropertyOrField(body, member); return body;【参考方案3】:

在我看来,您正在尝试重写 Microsoft DynamicQuery。为什么不直接使用它呢?

这是一个例子:

IQueryable<Foo> myQuery = GetHibernateQuery();
myQuery = myQuery.OrderBy("address.street");

【讨论】:

因为我只得到了一个已经包含一堆数据的 IQueryable 对象,然后我添加了分页等等,之后我只加载了查询中剩下的数据。这是由 nHibernate 完成的。仅用于排序它不能成为解决方案(我希望) 这就是我的观点。 DynamicQuery 已经可以做到这一点“将排序添加到现有的可查询对象中”。我的博客上有一个演示解决方案,它就是这样做的。 blogs.teamb.com/craigstuntz/2009/04/27/38243 我在帖子中添加了一个简单的例子。【参考方案4】:

试试这个

    public static IQueryable<T> SortIQueryable<T>(IQueryable<T> data, string fieldName, string sortOrder)
    
        if (string.IsNullOrWhiteSpace(fieldName)) return data;
        if (string.IsNullOrWhiteSpace(sortOrder)) return data;

        var param = Expression.Parameter(typeof(T), "i");

        MemberExpression property = null;
        string[] fieldNames = fieldName.Split('.');
        foreach (string filed in fieldNames)
        
            if (property == null)
            
                property = Expression.Property(param, filed);
            
            else
            
                property = Expression.Property(property, filed);
            
        

        Expression conversion = Expression.Convert(property, typeof(object));//Expression.Property(param, fieldName)
        var mySortExpression = Expression.Lambda<Func<T, object>>(conversion, param);

        return (sortOrder == "desc") ? data.OrderByDescending(mySortExpression)
            : data.OrderBy(mySortExpression);
    

【讨论】:

以上是关于使用 Linq.Expression 访问具有动态 lambda 的嵌套属性的主要内容,如果未能解决你的问题,请参考以下文章

具有访问权限的asp.net动态数据

如果我们在输入 ksds 中使用具有动态访问模式的备用键概念,是不是可以写入输出 ksds?

对具有“易失性”属性的动态分配变量的内存访问是不是会导致每次访问的缓存未命中?

在 Typescript 中访问具有动态键名的状态 [重复]

检查/访问 QML 中动态创建的对象

Terraform:如何创建具有动态和静态内容的块