Dynamic linq order by on nested property with null

2019-02-16 13:07发布

I'm using this dynamic linq orderby function which I got from here.

This works fine with nested properties so I could do this:

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

The problem is that if SomeProperty is null then performing the OrderBy on the NestedProperty throws the infamous "Object reference not set to an instance of an object".

My guess is that I need to customize the following lines to handle the exception:

expr = Expression.Property(expr, pi);

// Or

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

I thought about creating a statement body where I could in the worst case scenario use a try catch but that didn't work as you can't have statement bodies within orderby linq statements: "A lambda expression with a statement body cannot be converted to an expression tree"

I'm lost over here, any suggestions on how I can accomplish this?

By the way, this is for Linq to Objects, not database related.

2条回答
何必那么认真
2楼-- · 2019-02-16 13:42

How about generics:

Helper Method:

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

Sample Hierarchy:

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

Example:

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

Output:

3
2
1
Null
Null
查看更多
爷的心禁止访问
3楼-- · 2019-02-16 13:47
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;
    }
}
查看更多
登录 后发表回答