複雑なモデルにバインドするときに、すべてのパブリックプロパティが処理され、それぞれに対して一致バインドが試行されたという印象を受けました。
いいえ、それは間違った印象です。デフォルトのモデルバインダーは、リクエストに対応する値があるプロパティのみをバインドしようとします。あなたの場合、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;
}
}