【问题标题】:Access nested properties with dynamic lambda using Linq.Expression使用 Linq.Expression 访问具有动态 lambda 的嵌套属性
【发布时间】: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)

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

提前致谢。

【问题讨论】:

    标签: asp.net-mvc linq lambda


    【解决方案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);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-21
      相关资源
      最近更新 更多