LINQ Expression: Accessing The Field/Property Of A Sub-Object

dipique

Member
Joined
Oct 22, 2014
Messages
7
Programming Experience
1-3
At a high level, the problem I'm trying to solve is:

Given a Type (targetType) and the name of a property or field (targetField), generate a LINQ expression that retrieves the value of that field/property.

In the most common use case, it's pretty easy:

C#:
Expression.PropertyOrField(Expression.Parameter(targetType), targetField);

The problem is when I try to access a sub-class property (such as Order.Customer.Name). I can't access it directly using Expression.PropertyOrField, so I'm trying to build the expression. My code:

C#:
        private static MemberExpression GetPropertyOrField(string targetField, Type targetType)
        {
            //validation and get the different levels
            if (targetField == string.Empty) return null;
            const char sepChar = '.';
            string[] qualifiedFieldLevels = targetField.Split(sepChar);

            //Calculate the first targetField identifier that is not simply a qualification (e.g. if the targetType
            //is "Order", then in the targetField "Order.Customer", "Order" would be a qualification). In the targetField
            //"Order.Customer.Name", this would be (1), representing the second item, "Customer".  
            int startIndex = 0;
            foreach (var s in targetType.Name.Split(sepChar))
                if (s == qualifiedFieldLevels[startIndex]) startIndex++;

            //if the qualification removal leaves no target field, return null
            if (startIndex >= qualifiedFieldLevels.Count()) return null;


            //get the unqualified field levels and their types.  The Skip/Take part just means "skip x and take the rest".
            List<Tuple<Type, string>> fieldLevels = new List<Tuple<Type, string>>(); //the return type (Item1) and name (Item2)
            var lastType = targetType;
            foreach (string s in qualifiedFieldLevels.Skip(startIndex).Take(qualifiedFieldLevels.Count()))
            {
                var type = GetPropertyOrFieldType(lastType, s); //gets the type of the field/property "s" in type lastType
                fieldLevels.Add(new Tuple<Type, string>(type, s));
                lastType = type;
            }

            //if it's a simple property or field (read: only one level), take care of it the easy way
            if (fieldLevels.Count() == 1)
                return Expression.PropertyOrField(Expression.Parameter(targetType), fieldLevels[0].Item2);

            //Otherwise, get the property access expression so we can start assembling the expression
            //Note that we subtract two from the fieldLevels index. One is bc of 0-based index, the other is bc if the current
            //level holds return type, then the previous level holds the input parameter type.
            ParameterExpression p = Expression.Parameter(fieldLevels[fieldLevels.Count() - 2].Item1);
            var retVal = Expression.PropertyOrField(p, fieldLevels.Last().Item2);                     
                                                                                                      
            //Assemble the levels to get us to the resultType
            for (int x = fieldLevels.Count() - 2; x >= 0; x--) //loop backward through the parameters except the highest level
            {
                Type t = x == 0 ? targetType : fieldLevels[x - 1].Item1;
                p = Expression.Parameter(t); // the input parameter 
                retVal = Expression.PropertyOrField(Expression.Lambda(t, retVal, new ParameterExpression[] { p }), fieldLevels[x].Item2); // << error received on this line
            }

            //then return the result!
            return retVal;
        }

However, when my code encounter a target field with a sub-class, I receive the error message; "Lambda type parameter must be derived from System.Delegate". I'm already working a little out of my depth, so I'm having trouble figuring out how to fix this. Any help would be appreciated.

The overarching project is a little wider; I'm working on a generalized search object that generates linq queries from search filters. However, if it helps, you can download my full class file (http://1drv.ms/1Q627Ji), which includes a TestClass that creates fake data and implements the search object. If you throw it in a new Visual Studio Console project and create an instance of the TestClass object, it will produce the error without any additional configuring.

Thank you!

Daniel
 
Back
Top Bottom