1

This is my code which generates property of T object by property string.

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

Next, I use it here:

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

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

It works good when property is not null. I have problem with example:

property = "Customers.Dicts.DictValue"

in T object Customers property can be null either Customers.Dicts property can be.

What should I add to GetPropertyFunc method to make checks of null? I don't know where and how to put condition != null or .HasValue.

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
Staly
  • 85
  • 7

2 Answers2

1

As presented in this post:

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

I'd suggest changing the foreach loop to check for default values (null) constructed as an expression in respect to value types too.

Here is a little help for construction default of value type:

Programmatic equivalent of default(Type)

Here is the Unit test:

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

    }
}
Community
  • 1
  • 1
Attila Horváth
  • 562
  • 1
  • 5
  • 16
  • I have changed from body.Type -> prop.Type in the Expression.Condition Expression ifTrue parameter. The error was related to when the condition evaluates to true it returns other datatype than when it evaluates to false. – Attila Horváth Dec 01 '16 at 13:22
  • Please provide your complete test case, so I can check why the problem persists. – Attila Horváth Dec 01 '16 at 13:25
  • 1
    I solved this myself. There was an error when `property` string was single entry, for ex. "Colour" not "Customers.Dicts.DictValue". I if'ed that - when Split.Count() > 0 - I use your code, if not - I use previous. – Staly Dec 01 '16 at 13:31
  • I noticed that there is an error when prop.Type is Guid: "Incorrect argument types". Can sb help me? – Staly Dec 09 '16 at 12:27
  • I updated my answer in regards to your question. Also I don't seem to have the error with single entries. – Attila Horváth Dec 09 '16 at 20:08
0

You need to check for null values on those properties before you call GetPropertyFunc method. This way, the GetPropertyFunc method will return a lambda which will not have those properties in it.

Honestly, I am not sure what you are trying to do except that you are trying to create a query which you will finally use somewhere. Perhaps there is a better way of achieving what you want if we knew your goal and intention.

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
  • I don't know about nulls before that method because I get string which has to be converted to property. – Staly Dec 01 '16 at 12:15
  • You need to analyse the string and figure out if your target object has those properties and ensure those properties are not null. Then create the lambda. If you have the lambda before checking for nulls, then once you figure out a property is null, you need to somehow remove it from your lambda; this is why you should check for nulls first. There is not other way. – CodingYoshi Dec 01 '16 at 12:23