【问题标题】:Linq: OrderBy dynamic nested property which can be nullLinq:OrderBy 动态嵌套属性,可以为空
【发布时间】:2017-04-16 01:05:40
【问题描述】:

这是我的代码,它通过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"

在T对象中Customers属性可以为null或者Customers.Dicts属性都可以。

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

【问题讨论】:

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


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

    【讨论】:

    • 我已经从 Expression.Condition Expression ifTrue 参数中的 body.Type -> prop.Type 更改了。该错误与条件评估为 true 时返回的数据类型与评估为 false 时不同。
    • 请提供您完整的测试用例,以便我检查问题仍然存在的原因。
    • 我自己解决了这个问题。当property 字符串为单个条目时出现错误,例如。 “颜色”不是“Customers.Dicts.DictValue”。我认为 - 当 Split.Count() > 0 - 我使用你的代码,如果不是 - 我使用以前的。
    • 我注意到 prop.Type 为 Guid 时出现错误:“参数类型不正确”。某人可以帮助我吗?
    • 我更新了关于您的问题的答案。此外,我似乎没有单个条目的错误。
    【解决方案2】:

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

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

    【讨论】:

    • 我不知道该方法之前的空值,因为我得到了必须转换为属性的字符串。
    • 您需要分析字符串并确定您的目标对象是否具有这些属性并确保这些属性不为空。然后创建 lambda。如果你在检查空值之前有 lambda,那么一旦你确定一个属性为空,你需要以某种方式从你的 lambda 中删除它;这就是为什么您应该首先检查空值的原因。没有其他办法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多