类型为List <Expression <Func <T,dynamic >>>

时间:2019-08-12 15:14:05

标签: asp.net-mvc-5.2

我正尝试将视图集合的表达式提交给控制器。视图模型是这样的:

public class Model<T> {

  public List<Expression<Func<T, dynamic>>> Expressions {get; set;};

}

理想情况下,我想用一个属性标记Expressions成员,该属性指示模型绑定程序用来反序列化集合中的每个Expressions。

我认为具有挑战性的部分是对表达式进行反序列化,但是目前我很困惑,只是想出如何创建上述属性的方法。这里和互联网上的一些答案描述了这样做的方法,但是,它们似乎使用的不是我正在使用的MS MVC的某些版本。他们似乎都假定IModelBinder定义了一个BindProperty对象作为其参数之一的PropertyDescriptor方法。在我所使用的版本中似乎没有此类成员。

我认为默认模型绑定程序可以正确处理数据绑定到集合的事实,因为结果集合具有正确数量的条目,但它们全为空。这就是让我认为潜在的问题是默认的资料夹不知道如何处理看起来像x => x.MyMember的东西,如果可以附加自定义属性资料夹属性,我想我可以理解。某种。

1 个答案:

答案 0 :(得分:0)

我最终通过为lambda表达式周围的包装创建自定义类型转换器来解决了这个问题。在类型转换器中,我使用NuGet上可用的System.Linq.Dynamic来解析lambda表达式并将其映射回视图模型。相关片段如下:

public class Model<T> {

  public List<Expression<Func<T, dynamic>>> Expressions {get; set;};

}

成为:

public class Model<T> {

  public List<ColumnSelector<T>> Expressions {get; set;};

}

我将有问题的表达式包装在一个简单的包装器类中,如下所示:

[TypeDescriptionProvider(typeof(ExpressionDescriptorProvider))]
public class ColumnSelector <T>
{
    public Expression<Func<T, dynamic>> Expression { get; set; }

    public override string ToString()
    {
        return this.Expression.ToString();
    }
}

一旦实现了TypeDescriptorProviderExpressionDescriptorProvider,模型绑定就可以像您期望的那样工作。

public class ExpressionConverter<T> : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        //i'm essentially desserializing from a string, so we return true in that case
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var stringValue = value as string;

        if (!string.IsNullOrEmpty(stringValue))
        {
            string[] parts = stringValue.Split(new[] {" => "}, StringSplitOptions.RemoveEmptyEntries);

            string param = parts[0];
            string expression = parts[1];

            bool nullable = false;
            if (expression.Contains("Convert"))
            {
                //any exceptions originating in this method may be indicative that more
                //conditions need to be tested and handled here
                expression = expression.Replace("Convert(", string.Empty).Replace(")", string.Empty);
                nullable = true;
            }

            ParameterExpression p = Expression.Parameter(typeof(T), param);

            //this line is from the above mentioned NuGetPackage
            LambdaExpression lambdaExpression = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, expression);

            Expression body = lambdaExpression.Body;

            if (nullable)
            {
                //this is a special case for handling members which are nullable,
                //there may be other special cases which we need to address
                body = Expression.Convert(body, typeof(Object));
            }

            Expression<Func<T, dynamic>> lamda = Expression.Lambda<Func<T, dynamic>>(body, p);

            ColumnSelector<T> obj = new ColumnSelector<T> { Expression = lamda };

            return obj;
        }

        throw new Exception(string.Format("Unable to convert object {0} to ColumnSelector<T>.", value));
    }
}

然后我们需要一些样板来将它们全部连接起来:

public class ExpressionDescriptor : CustomTypeDescriptor
{
    private readonly Type _objectType;

    public ExpressionDescriptor(Type objectType)
    {
        this._objectType = objectType;
    }

    public override TypeConverter GetConverter()
    {
        var genericArg = _objectType.GenericTypeArguments[0];
        var converterType = typeof(ExpressionConverter<>).MakeGenericType(genericArg);
        return (TypeConverter)Activator.CreateInstance(converterType);
    }
}

public class ExpressionDescriptorProvider : TypeDescriptionProvider
{
    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    {
        return new ExpressionDescriptor(objectType);
    }
}