绑定复杂类型而不覆盖ToString

时间:2014-01-02 11:05:12

标签: c# asp.net-mvc html-helper model-binding

我正在使用自定义模型绑定器来绑定我的复杂类型。

以下是模型:

[ModelBinder(typeof(SupplierModelBinder))]
public class SupplierModel
{
    public string VendorId { get; set; }

    public string VendorName { get; set; }

    public override string ToString()
    {
        return VendorId;
    }
}

这是活页夹:

public class SupplierModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        string key = bindingContext.ModelName;
        ValueProviderResult val = bindingContext.ValueProvider.GetValue(key);
        if (val != null)
        {
            string s = val.AttemptedValue as string;
            if (s != null)
            {
                return new SupplierModel() { VendorId = s };
            }
        }

        return null;
    }
}

我使用Html.ActionLink使用UserModel的{​​{1}}方法渲染模型。当ToString到服务器时,它使用结果来绑定模型。

这很好用(我对这里的GET属性并不感到烦恼),但它依赖于覆盖模型类中的VendorName,因此我可以在模型绑定器中使用该值。 / p>

如何将复杂类型的绑定/解除绑定与ToString方法分开?

我不想重写ToString只是为了让我的模型正确呈现以供我的活页夹解释。对于其他类型,我必须(de)序列化为JSON或simiar,我不想在ToString

1 个答案:

答案 0 :(得分:0)

我设法在不使用自定义绑定的情况下弄清楚如何执行此操作。诀窍是要意识到如果我将Supplier.VendorId="XXXX"之类的东西作为动作链接的查询字符串的一部分进行渲染,那么它就会被正确映射。

我使用反射来查看HtmlHelper.ActionLink()在传递对象时所执行的操作,即创建RouteValueDictionary<string, object>的实例,该实例为对象的每个属性创建一个键。

这个默认实现接近我想要的,但我需要它来处理属性的属性。

幸运的是,ActionLink()超载会直接占用RouteValueDictionary<string, object>,因此我遇到了使用Property.SubProperty类型键正确构建一个的问题。

我最终得到了以下代码,该代码首先使用RouteValueDictonary的构造函数来获取每个属性的密钥。
然后它根据Bind属性(如果类有一个属性)删除任何不会被绑定的东西,它会对结果查询字符串进行整理。

它的主要部分是查找...Model类型的任何属性(类型名称以&#34结尾; Model&#34;)并将该对象的属性添加到字典中。我需要使用一些规则来判断是否递归,否则它会尝试去处理对象列表等事物的属性。我认为我已经使用了我的模型类的约定,所以我可以坚持它


public static RouteValueDictionary ToRouteValueDictionary(this object obj)
{
    var Result = new RouteValueDictionary(obj);

    // Find any ignored properties
    var BindAttribute = (BindAttribute)obj.GetType().GetCustomAttributes(typeof(BindAttribute), true).SingleOrDefault();

    var ExcludedProperties = new List<string>();

    if (BindAttribute != null)
    {
        ExcludedProperties.AddRange(BindAttribute.Exclude.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
    }

    // Remove any ignored properties from the dictionary
    foreach (var ExcludedProperty in ExcludedProperties)
    {
        Result.Remove(ExcludedProperty);
    }

    // Loop through each property, recursively adding sub-properties that end with "Model" to the dictionary
    foreach (var Property in obj.GetType().GetProperties())
    {
        if (ExcludedProperties.Contains(Property.Name))
        {
            continue;
        }

        if (Property.PropertyType.Name.EndsWith("Model"))
        {
            Result.Remove(Property.Name);

            var PropertyValue = Property.GetValue(obj, null);

            if (PropertyValue != null)
            {
                var PropertyDictionary = PropertyValue.ToRouteValueDictionary();

                foreach (var Key in PropertyDictionary.Keys)
                {
                    Result.Add(string.Format("{0}.{1}", Property.Name, Key), PropertyDictionary[Key]);
                }
            }
        }
    }

    return Result;
}