LINQ表达式:访问子对象的字段/属性

迪皮克

成员
已加入
2014年10月22日
留言内容
7
编程经验
1-3
从高层次来看,我要解决的问题是:

给定类型(targetType)和属性或字段的名称(targetField),生成LINQ表达式,该表达式检索该字段/属性的值。

在最常见的用例中,这很简单:

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

问题是当我尝试访问子类属性(例如Order.Customer.Name)时。我无法使用Expression.PropertyOrField直接访问它,因此我正在尝试构建表达式。我的代码:

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

但是,当我的代码遇到带有子类的目标字段时,会收到错误消息。"Lambda类型参数必须从System.Delegate派生"。我已经超出了我的深度,因此无法确定解决方法。任何帮助,将不胜感激。

总体项目要宽一些。我正在研究一种可从搜索过滤器生成linq查询的通用搜索对象。但是,如果有帮助,您可以下载我的完整课程文件(http://1drv.ms/1Q627Ji),其中包括一个TestClass,该类创建伪造的数据并实现搜索对象。如果将其扔到新的Visual Studio Console项目中并创建TestClass对象的实例,它将在没有任何其他配置的情况下产生错误。

谢谢!

丹尼尔
 
最佳 底部