2013-01-14 69 views
4

我的印象是,綁定到複雜模型時,所有公共屬性都被處理,並嘗試爲每個屬性進行匹配綁定。下劃線字符串模型綁定器

我想,這樣一個模型

class Model { 
     public string Foo {get;set;} 
     public string FooBar {get;set;} 
} 

作品很好地與下面的查詢字符串

?foo=foo&foo_bar=foo_bar 

難道還有比一個自定義的模型綁定一個更好的辦法來解決一個變量的命名問題?無論如何,我的工作不起作用。 FooBar簡單地被跳過。

public class StringModelBinder : DefaultModelBinder 
    { 
     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      var model = base.BindModel(controllerContext, bindingContext); 

      if (model != null) 
       return model; 

      var modelName = Regex.Replace(bindingContext.ModelName, "([a-z])([A-Z])", "$1_$2").ToLowerInvariant(); 

      var value = bindingContext.ValueProvider.GetValue(modelName); 

      return value; 
     } 

    } 

登記

ModelBinders.Binders.Add(typeof(string), new StringModelBinder()); 

回答

15

我的印象是,綁定到一個複雜的模型時,所有 公共屬性的處理,並嘗試結合每個 匹配下。

不,這是一個錯誤的印象。默認模型聯編程序將嘗試僅綁定您在請求中具有相應值的屬性。在你的情況下,你沒有相應的FooBar屬性值,所以它不會被綁定。

實際上這將是很好,如果我們可以這樣寫:

public class Model 
{ 
    public string Foo { get; set; } 

    [ParameterName("foo_bar")] 
    public string FooBar { get; set; } 
} 

讓我們實現這一點。首先,我們寫一個基本屬性:

[AttributeUsageAttribute(AttributeTargets.Property)] 
public abstract class PropertyBinderAttribute : Attribute, IModelBinder 
{ 
    public abstract object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); 
} 

和一個自定義的模型綁定:

public class CustomModelBinder : DefaultModelBinder 
{ 
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) 
    { 
     var propertyBinderAttribute = propertyDescriptor 
      .Attributes 
      .OfType<PropertyBinderAttribute>() 
      .FirstOrDefault(); 

     if (propertyBinderAttribute != null) 
     { 
      var value = propertyBinderAttribute.BindModel(controllerContext, bindingContext); 
      propertyDescriptor.SetValue(bindingContext.Model, value); 
     } 
     else 
     { 
      base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
     } 
    } 
} 

正如你可以看到這個自定義模型分析模型的元數據,如果一個屬性裝飾有一個實例PropertyBinderAttribute將使用它。

我們將在Application_Start與我們的習慣之一,然後替換默認模型綁定:

ModelBinders.Binders.DefaultBinder = new CustomModelBinder(); 

現在所有剩下的就是落實我們用來裝飾我們的模型屬性與ParameterNameAttribute粘結劑:

public class ParameterNameAttribute : PropertyBinderAttribute 
{ 
    private readonly string parameterName; 
    public ParameterNameAttribute(string parameterName) 
    { 
     this.parameterName = parameterName; 
    } 

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var value = bindingContext.ValueProvider.GetValue(this.parameterName); 
     if (value != null) 
     { 
      return value.AttemptedValue; 
     } 
     return null; 
    } 
} 
+2

謝謝(再次)Darin!那簡直就是美麗。 – Martin

+3

+1:當我長大後,我想能夠發佈這樣的答案! –

+0

有一天,我會加總達林多年來爲我節省的時間! –