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

时间:2016-12-01 11:46:35

标签: c# linq dynamic sql-order-by func

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

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

当属性不为null时,它工作正常。 我的例子有问题:

property = "Customers.Dicts.DictValue"

T对象Customers属性中的

可以为Customers.Dicts属性为空。

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

2 个答案:

答案 0 :(得分:1)

如本文所述:

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

    }
}

答案 1 :(得分:0)

在调用GetPropertyFunc方法之前,您需要检查这些属性的空值。这样,GetPropertyFunc方法将返回一个lambda,其中不包含那些属性。

老实说,我不确定你要做什么,除了你试图创建一个你最终会在某个地方使用的查询。如果我们知道你的目标和意图,也许有更好的方法来实现你想要的东西。