2013-01-14 37 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

有一天,我会加总达林多年来为我节省的时间! –