如何在 Asp.Net MVC 4 的多态模型中绑定复杂属性?
How to bind complex properties within polymorphic model in Asp.Net MVC 4?
我需要创建一个基于派生类型的动态输入表单,但是当传递到我的控制器的 POST 方法时,我无法正确绑定复杂的属性。其他属性绑定良好。这是我所拥有的一个人为的例子:
型号
public abstract class ModelBase {}
public class ModelDerivedA : ModelBase
{
public string SomeProperty { get; set; }
public SomeType MySomeType{ get; set; }
public ModelDerivedA()
{
MySomeType = new SomeType();
}
}
public class SomeType
{
public string SomeTypeStringA { get; set; }
public string SomeTypeStringB { get; set; }
}
自定义模型活页夹
活页夹基于这个答案:polymorphic-model-binding
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
var type = Type.GetType(
(string)typeValue.ConvertTo(typeof(string)),
true
);
if (!typeof(ModelBase).IsAssignableFrom(type))
{
throw new InvalidOperationException("The model does not inherit from mode base");
}
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
控制器
[HttpPost]
public ActionResult GetDynamicForm([ModelBinder(typeof(BaseViewModelBinder))] ModelBase model)
{
// model HAS values for SomeProperty
// model has NO values for MySomeType
}
查看摘录
@Html.Hidden("ModelType", Model.GetType())
@Html.Test(Model);
JavaScript
表单是使用 $.ajax
使用 data: $(this).serialize()
发布的,如果我调试它会显示正确填充的表单数据。
除 SomeType
之外的所有属性都填充到模型中。我需要更改什么才能填充它们?
谢谢
尝试将默认构造函数添加到您的 ModelDerivedA
以初始化 MySomeType
public class ModelDerivedA : ModelBase
{
public ModelDerivedA()
{
MySomeType = new SomeType();
}
}
没有填充值,因为您正在创建如下类型的新实例:
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
并返回相同的模型,这是不正确的。
执行如下操作。
ValueProviderResult valueResult;
bindingContext.ModelState.SetModelValue("ModelType", valueResult);
return valueResult;
这里对 modelBinder 的讨论非常好。
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
我通过以下方式解决了眼前的问题:
- 获取
FormvalueProvider
的实例(以访问已 posted 的内容)
递归遍历我的模型并将每个 属性 值设置为 FormValueProvider
中的匹配值
private FormValueProvider vp;
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
var type = Type.GetType(
(string)typeValue.ConvertTo(typeof(string)),
true
);
if (!typeof(ModelBase).IsAssignableFrom(type))
{
throw new InvalidOperationException("Bad Type");
}
var model = Activator.CreateInstance(type);
vp = new FormValueProvider(controllerContext);
bindingContext.ValueProvider = vp;
SetModelPropertValues(model);
return model;
}
和递归,基于这个答案here打印嵌套对象中的属性
private void SetModelPropertValues(object obj)
{
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, null);
var elems = propValue as IList;
if (elems != null)
{
foreach (var item in elems)
{
this.SetModelPropertValues(item);
}
}
else
{
if (property.PropertyType.Assembly == objType.Assembly)
{
this.SetModelPropertValues(propValue);
}
else
{
property.SetValue(obj, this.vp.GetValue(property.Name).AttemptedValue, null);
}
}
}
}
任何使用它的人都可能需要使其更强大以满足他们的需求。
我很想知道这种作为解决此类问题的一般方法的任何缺点。
不过,我希望这个 post 在某些情况下有所帮助。
我需要创建一个基于派生类型的动态输入表单,但是当传递到我的控制器的 POST 方法时,我无法正确绑定复杂的属性。其他属性绑定良好。这是我所拥有的一个人为的例子:
型号
public abstract class ModelBase {}
public class ModelDerivedA : ModelBase
{
public string SomeProperty { get; set; }
public SomeType MySomeType{ get; set; }
public ModelDerivedA()
{
MySomeType = new SomeType();
}
}
public class SomeType
{
public string SomeTypeStringA { get; set; }
public string SomeTypeStringB { get; set; }
}
自定义模型活页夹
活页夹基于这个答案:polymorphic-model-binding
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
var type = Type.GetType(
(string)typeValue.ConvertTo(typeof(string)),
true
);
if (!typeof(ModelBase).IsAssignableFrom(type))
{
throw new InvalidOperationException("The model does not inherit from mode base");
}
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
控制器
[HttpPost]
public ActionResult GetDynamicForm([ModelBinder(typeof(BaseViewModelBinder))] ModelBase model)
{
// model HAS values for SomeProperty
// model has NO values for MySomeType
}
查看摘录
@Html.Hidden("ModelType", Model.GetType())
@Html.Test(Model);
JavaScript
表单是使用 $.ajax
使用 data: $(this).serialize()
发布的,如果我调试它会显示正确填充的表单数据。
除 SomeType
之外的所有属性都填充到模型中。我需要更改什么才能填充它们?
谢谢
尝试将默认构造函数添加到您的 ModelDerivedA
以初始化 MySomeType
public class ModelDerivedA : ModelBase
{
public ModelDerivedA()
{
MySomeType = new SomeType();
}
}
没有填充值,因为您正在创建如下类型的新实例:
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
并返回相同的模型,这是不正确的。
执行如下操作。
ValueProviderResult valueResult;
bindingContext.ModelState.SetModelValue("ModelType", valueResult);
return valueResult;
这里对 modelBinder 的讨论非常好。
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
我通过以下方式解决了眼前的问题:
- 获取
FormvalueProvider
的实例(以访问已 posted 的内容) 递归遍历我的模型并将每个 属性 值设置为
中的匹配值FormValueProvider
private FormValueProvider vp; protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { var typeValue = bindingContext.ValueProvider.GetValue("ModelType"); var type = Type.GetType( (string)typeValue.ConvertTo(typeof(string)), true ); if (!typeof(ModelBase).IsAssignableFrom(type)) { throw new InvalidOperationException("Bad Type"); } var model = Activator.CreateInstance(type); vp = new FormValueProvider(controllerContext); bindingContext.ValueProvider = vp; SetModelPropertValues(model); return model; }
和递归,基于这个答案here打印嵌套对象中的属性
private void SetModelPropertValues(object obj)
{
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, null);
var elems = propValue as IList;
if (elems != null)
{
foreach (var item in elems)
{
this.SetModelPropertValues(item);
}
}
else
{
if (property.PropertyType.Assembly == objType.Assembly)
{
this.SetModelPropertValues(propValue);
}
else
{
property.SetValue(obj, this.vp.GetValue(property.Name).AttemptedValue, null);
}
}
}
}
任何使用它的人都可能需要使其更强大以满足他们的需求。
我很想知道这种作为解决此类问题的一般方法的任何缺点。
不过,我希望这个 post 在某些情况下有所帮助。