【问题标题】:Dynamic MemberExpression动态成员表达式
【发布时间】:2011-07-16 08:33:30
【问题描述】:

我想创建一个只知道字段名称的 MemberExpression;例如:

public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>(string fieldName)
    {
        PropertyInfo fieldPropertyInfo;

        fieldPropertyInfo = typeof(TModel).GetProperty(fieldName);

        var entityParam = Expression.Parameter(typeof(TModel), "e"); // {e}
        var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
        var lambda = Expression.Lambda(columnExpr, entityParam) as Expression<Func<TModel, T>>; // {e => e.column}

        return lambda;
    }

上面的问题是字段类型必须是强类型的。将“对象”作为字段类型传入是行不通的。有没有办法生成这个?甚至动态 LINQ 似乎也不起作用。

【问题讨论】:

    标签: c# linq dynamic expression-trees


    【解决方案1】:

    您的代码存在许多问题:

    1. 您的方法的参数称为fieldName,但您正在使用它获得一个属性
    2. 您正在使用非泛型Expression.Lambda 方法生成表达式,如果传递给该方法的类型参数T 与属性类型不同,则该表达式可能会选择不合适的委托类型。在这种情况下,从表达式到方法的返回类型的as 转换将失败并计算为null。解决方案:使用带有适当类型参数的generic Lambda 方法。无需投射。
    3. 如果您解决了第二个问题,当可以从属性类型到T 的安全引用转换可用时,一切都会正常工作,但在需要更复杂的转换(例如拳击/举重)时就不行了。解决方案:必要时使用Expression.Convert 方法。

    以下是解决这些问题的示例更新:

    public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>
       (string propertyName)
    {
        var propertyInfo = typeof(TModel).GetProperty(propertyName);
    
        var entityParam = Expression.Parameter(typeof(TModel), "e"); 
        Expression columnExpr = Expression.Property(entityParam, propertyInfo);
    
        if (propertyInfo.PropertyType != typeof(T))
            columnExpr = Expression.Convert(columnExpr, typeof(T));
    
        return Expression.Lambda<Func<TModel, T>>(columnExpr, entityParam);
    }
    

    这将使以下所有调用成功:

    GenerateMemberExpression<FileInfo, string>("Name");
    GenerateMemberExpression<string, int>("Length");
    
    // Reference conversion
    GenerateMemberExpression<FileInfo, object>("Name");          
    
    //Boxing conversion
    GenerateMemberExpression<string, object>("Length");
    
    //Lifted conversion
    GenerateMemberExpression<string, int?>("Length");
    

    【讨论】:

    • 谢谢@Ani。帮了我不少忙。对于我自己的代码,我还添加了一个采用 propertyInfo 而不是 propertyName 的方法。
    • 非常感谢!您的代码示例帮助我理解了表达式的构建,并允许我做我想做的事情:D
    【解决方案2】:

    在传递“对象”的情况下尝试手动转换字段值。例如:

    var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
    if (T.GetType().Equals(typeof(object)))
    {
        columnExpr = Expression.Convert(columnExpr, typeof(object));
    }
    

    希望这会对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多