如何在Asp.Net MVC 4中绑定多态模型中的复杂属性?

时间:2015-02-18 09:04:36

标签: c# model-view-controller model-binding custom-model-binder

我需要基于派生类型创建动态输入表单,但是当传递给我的控制器的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的属性。我需要更改什么来填充它们?

由于

3 个答案:

答案 0 :(得分:1)

未填充值,因为您正在创建类型如下的新实例:

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

答案 1 :(得分:0)

尝试向ModelDerivedA添加默认构造函数以初始化MySomeType

public class ModelDerivedA : ModelBase
{
    public ModelDerivedA()
    {
        MySomeType = new SomeType();
    }
}

答案 2 :(得分:0)

我通过以下方式解决了我的问题:

  1. 获取FormvalueProvider的实例(以访问已发布的内容)
  2. 递归浏览我的模型并将每个属性值设置为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;        
    }
    
  3. 基于此答案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);
                    }
                }
            }
        }
    

    使用它的任何人都可能需要使其更加健壮以满足他们的需求。

    作为解决此类问题的一般方法,我会非常有兴趣听到这方面的任何弊端。

    但是,我希望这篇文章在某些情况下有所帮助。