Linq:OrderBy 动态嵌套 属性 可以为空

Linq: OrderBy dynamic nested property which can be null

这是我的代码,它通过 property 字符串生成 属性 个 T 对象。

// returning property as lambda from string
public static Func<T, object> GetPropertyFunc<T>(string property)
{
    try
    {
        var parameter = Expression.Parameter(typeof(T), "obj");

        Expression body = parameter;
        foreach (var member in property.Split('.'))
        {
            body = Expression.PropertyOrField(body, member);
        }              

        // conversion from Toutput to object
        Expression converted = Expression.Convert(body, typeof(object));

        return Expression.Lambda<Func<T, object>>(converted, parameter).Compile();

        //return (Func<T, object>)Expression.Lambda(body, parameter).Compile();
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

接下来,我在这里使用它:

var orderParamFunc = PagedListHelper.GetPropertyFunc<T>(pagedListModel.OrderParameter.ParameterName);

IOrderedEnumerable<T> finalQuery = pagedListModel.OrderParameter.OrderAscending ? whereQuery.OrderBy(orderParamFunc) : whereQuery.OrderByDescending(orderParamFunc);

当 属性 不为空时效果很好。 我对示例有疑问:

property = "Customers.Dicts.DictValue"

in T object Customers 属性 可以是null Customers.Dicts 属性 也可以.

我应该在 GetPropertyFunc 方法中添加什么来检查 null?我不知道在哪里以及如何放置条件 != null.HasValue.

您需要在调用 GetPropertyFunc 方法之前检查这些属性的空值。这样,GetPropertyFunc 方法将 return 一个没有这些属性的 lambda。

老实说,我不确定您要做什么,只是您正在尝试创建一个您最终将在某处使用的查询。如果我们知道您的目标和意图,也许有更好的方法来实现您想要的。

如本 post 所示:

How to detect IsNull / NotNull when building dynamic LINQ expressions?

我建议更改 foreach 循环以检查构造为关于值类型的表达式的默认值 (null)。

这里有一点帮助构造值类型的默认值:

Programmatic equivalent of default(Type)

这是单元测试:

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq.Expressions;

namespace TestProject1
{
    [TestClass]
    public class UnitTest1
    {
        class TestRefType2
        {
            public TestRefType2()
            {

            }
        }

        class TestRefType1
        {
            public TestRefType1()
            {

            }

            public Guid VALUETYPE { get; set; }
            public TestRefType2 REFTYPE { get; set; }
        }

        class MainType
        {
            public MainType()
            {

            }

            public TestRefType1 REFTYPE { get; set; }
        }

        public static object GetDefault(Type type)
        {
            if (type.IsValueType)
            {
                return Activator.CreateInstance(type);
            }
            return null;
        }

        // returning property as lambda from string
        public static Func<T, object> GetPropertyFunc<T>(string property)
        {
            try
            {
                var parameter = Expression.Parameter(typeof(T), "obj");

                Expression body = parameter;
                foreach (var member in property.Split('.'))
                {
                    var prop = Expression.PropertyOrField(body, member);
                    body = Expression.Condition(Expression.Equal(body, Expression.Default(body.Type)), Expression.Default(prop.Type), prop);
                }

                // conversion from Toutput to object
                Expression converted = Expression.Convert(body, typeof(object));

                return Expression.Lambda<Func<T, object>>(converted, parameter).Compile();

                //return (Func<T, object>)Expression.Lambda(body, parameter).Compile();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        [TestMethod]
        public void TestMethod1()
        {
            MainType t = new MainType();
            t.REFTYPE = new TestRefType1();

            Func<MainType, object> ex = GetPropertyFunc<MainType>("REFTYPE.VALUETYPE");
            object val = ex(t);

            Assert.AreEqual(default(Guid), val);
        }

        [TestMethod]
        public void TestMethod2()
        {
            MainType t = new MainType();

            Func<MainType, object> ex = GetPropertyFunc<MainType>("REFTYPE.VALUETYPE");
            object val = ex(t);

            Assert.AreEqual(default(Guid), val);
        }

        [TestMethod]
        public void TestMethod3()
        {
            MainType t = new MainType();
            t.REFTYPE = new TestRefType1();
            var guid = Guid.NewGuid();
            t.REFTYPE.VALUETYPE = guid;

            Func<MainType, object> ex = GetPropertyFunc<MainType>("REFTYPE.VALUETYPE");
            object val = ex(t);

            Assert.AreEqual(guid, val);
        }

        [TestMethod]
        public void TestMethod4()
        {
            MainType t = new MainType();
            t.REFTYPE = new TestRefType1();

            Func<MainType, object> ex = GetPropertyFunc<MainType>("REFTYPE");
            object val = ex(t);

            Assert.AreNotEqual(default(TestRefType1), val);
        }

        [TestMethod]
        public void TestMethod5()
        {
            MainType t = new MainType();
            t.REFTYPE = new TestRefType1();

            Func<MainType, object> ex = GetPropertyFunc<MainType>("REFTYPE.REFTYPE");
            object val = ex(t);

            Assert.AreEqual(default(TestRefType2), val);
        }

        [TestMethod]
        public void TestMethod6()
        {
            MainType t = new MainType();

            Func<MainType, object> ex = GetPropertyFunc<MainType>("REFTYPE.REFTYPE");
            object val = ex(t);

            Assert.AreEqual(default(TestRefType2), val);
        }

        [TestMethod]
        public void TestMethod7()
        {
            MainType t = new MainType();
            t.REFTYPE = new TestRefType1();
            var reftype2 = new TestRefType2();
            t.REFTYPE.REFTYPE = reftype2;

            Func<MainType, object> ex = GetPropertyFunc<MainType>("REFTYPE.REFTYPE");
            object val = ex(t);

            Assert.AreEqual(reftype2, val);
        }

    }
}