问题 具有null属性的嵌套属性上的动态linq顺序


我正在使用这个动态的linq orderby函数 这里

这适用于嵌套属性,所以我可以这样做:

var result = data.OrderBy("SomeProperty.NestedProperty");

问题是,如果SomeProperty为null,则在NestedProperty上执行OrderBy会抛出臭名昭着的“对象引用未设置为对象的实例”。

我的猜测是我需要自定义以下行来处理异常:

expr = Expression.Property(expr, pi);

// Or

LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);    

我考虑创建一个语句体,在最坏的情况下,我可以使用try catch,但这不起作用,因为你不能在orderby linq语句中有语句体:“带有语句体的lambda表达式无法转换为表达树“

我迷失在这里,有关如何实现这一目标的任何建议?

顺便说一句,这是针对Linq to Objects,而不是数据库相关的。


6440
2017-07-15 05:56


起源

我猜这一行 expr = Expression.Property(expr, pi); 套 expr 为null并且进一步的代码不处理它。解决它的最简单方法是 expr = Expression.Property(expr, pi) ?? default(T);。但是,在这种情况下,您需要检查是否可以使用已应用的订单。 - Tommi
这是一个好点,实际上会使排序工作错误,理想情况下,空值应该“分组”在一起。
看看这对你有什么帮助 stackoverflow.com/questions/41244/... - Ehsan
我想他们会:如果属性是字符串,则默认为String.Empty,并且所有具有null属性的项目将被推送到集合的后面或前面,具体取决于asc./desc。分类。 - Tommi
@Tommi我已经尝试了你的默认(T)来看它是如何工作但我无法编译它,我有一个“运算符??无法应用...”错误


答案:


static void Main(string[] args)
{
    var data = new List<MyType>() {
        new MyType() { SomeProperty = new Inner() { NestedProperty = "2" }},
        new MyType() { SomeProperty = new Inner() { NestedProperty = "1" }},
        new MyType() { SomeProperty = new Inner() { NestedProperty = "3" }},
        new MyType(),
    }.AsQueryable();
    var sorted = data.OrderBy(x => GetPropertyValue(x, "SomeProperty.NestedProperty"));

    foreach (var myType in sorted)
    {
       try
       {
          Console.WriteLine(myType.SomeProperty.NestedProperty);
       }
       catch (Exception e)
       {
          Console.WriteLine("Null");
       }
    }
}

public static object GetPropertyValue(object obj, string propertyName)
{
    try
    {
        foreach (var prop in propertyName.Split('.').Select(s => obj.GetType().GetProperty(s)))
        {
            obj = prop.GetValue(obj, null);
        }
        return obj;
    }
    catch (NullReferenceException)
    {
        return null;
    }
}

6
2017-07-15 09:19



出类拔萃,这个看起来比Marc Gravel的回答简单,这让我很奇怪。尽管如此,我的所有测试似乎都很好,你让我成为一个快乐的人。
非常聪明。递归的好方法。 Microsoft应该在未来的框架中将其作为扩展方法提供。 - arviman
如果不是SomeProperty你有List <SomeProperty>怎么办?如何修改上述代码来处理这种情况? - demonicdaron
@demonicdaron我不知道你想如何访问该属性,但假设你想要类似的东西 GetPropertyValue(x, "SomeProperty[0].NestedProperty"),你需要修改 GetPropertyValue 另外用方括号解析索引。然后,您可以在转换为的当前属性上使用此索引 IList, 我猜。 - Tommi


仿制药怎么样:

帮手方法:

public static Expression<Func<TEntity, TResult>> GetExpression<TEntity, TResult>(string prop)
        {
            var param = Expression.Parameter(typeof(TEntity), "p");
            var parts = prop.Split('.');

            Expression parent = parts.Aggregate<string, Expression>(param, Expression.Property);
            Expression conversion = Expression.Convert(parent, typeof (object));

            var tryExpression = Expression.TryCatch(Expression.Block(typeof(object), conversion),
                                                    Expression.Catch(typeof(object), Expression.Constant(null))); 

            return Expression.Lambda<Func<TEntity, TResult>>(tryExpression, param);
        }

示例层次结构:

public class A
    {
        public A(B b)
        {
            B = b;
        }

        public B B { get; set; }
    }

    public class B
    {
        public B(C c)
        {
            C = c;
        }

        public C C { get; set; }
    }

    public class C
    {
        public C(int id)
        {
            this.Id = id;
        }

        public int Id { get; set; }
    }

例:

var list = new List<B>
            { 
                new B(new A(new C(1))),
                new B(new A(new C(2))),
                new B(new A(new C(3))),
                new B(new A(null)),
                new B(null)
            }.AsQueryable();

var ordered = list.OrderByDescending(GetExpression<B, Object>("AProp.CProp.Id"));

输出:

3
2
1
Null
Null

3
2017-12-05 06:48



你是天才,非常感谢你! - Alexander