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:
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:
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
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