如何更新包含MVC3中IMyInterface列表的模型

时间:2011-07-01 02:20:45

标签: asp.net-mvc-3 data-binding

我有一个这样的模型:

        return new MyViewModel()
        {
            Name = "My View Model",

            Modules = new IRequireConfig[]
            {
                new FundraisingModule()
                {
                    Name = "Fundraising Module",
                    GeneralMessage = "Thanks for fundraising"
                },

                new DonationModule()
                {
                    Name = "Donation Module",
                    MinDonationAmount = 50
                }
            }
        };

IRequireConfig接口公开了一个DataEditor字符串属性,该视图用于传递给@ Html.EditorFor,如下所示:

    @foreach (var module in Model.Modules)
    {
        <div>
            @Html.EditorFor(i => module, @module.DataEditor, @module.DataEditor)  //the second @module.DataEditor is used to prefix the editor fields
        </div>
    }

当我将其发布回我的控制器时,TryUpdateModel将Modules属性保留为null。这是非常期待的,因为我不希望它知道要反序列化的具体类。

由于我在帖子进来时仍然可以使用原始模型,因此我可以循环使用模块并使用.GetType()获取其类型。似乎在这一点上我有足够的信息让TryUpdateModel尝试反序列化模型,但问题是它使用泛型类型推断来驱动反序列化器,因此它实际上不会更新任何属性,除了在接口

如何使用新值更新我的Modules数组?

如果有任何特殊要点不清楚请告诉我,我会尽力澄清

1 个答案:

答案 0 :(得分:1)

您可以使用自定义模型绑定器。假设您有以下型号:

public interface IRequireConfig
{
    string Name { get; set; }
}

public class FundraisingModule : IRequireConfig
{
    public string Name { get; set; }
    public string GeneralMessage { get; set; }
}

public class DonationModule : IRequireConfig
{
    public string Name { get; set; }
    public decimal MinDonationAmount { get; set; }
}

public class MyViewModel
{
    public string Name { get; set; }
    public IRequireConfig[] Modules { get; set; }
}

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            Name = "My View Model",
            Modules = new IRequireConfig[]
            {
                new FundraisingModule()
                {
                    Name = "Fundraising Module",
                    GeneralMessage = "Thanks for fundraising"
                },
                new DonationModule()
                {
                    Name = "Donation Module",
                    MinDonationAmount = 50
                }
            }
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

查看:

@model MyViewModel
@using (Html.BeginForm())
{
    @Html.EditorFor(x => x.Name)
    for (int i = 0; i < Model.Modules.Length; i++)
    {
        @Html.Hidden("Modules[" + i + "].Type", Model.Modules[i].GetType())
        @Html.EditorFor(x => x.Modules[i])
    }
    <input type="submit" value="OK" />
}

最后是自定义模型绑定器:

public class RequireConfigModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var typeParam = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
        if (typeParam == null)
        {
            throw new Exception("Concrete type not specified");
        }
        var concreteType = Type.GetType(typeParam.AttemptedValue, true);
        var concreteInstance = Activator.CreateInstance(concreteType);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => concreteInstance, concreteType);
        return concreteInstance;
    }
}

您将在Application_Start注册:

ModelBinders.Binders.Add(typeof(IRequireConfig), new RequireConfigModelBinder());

现在提交表单时,将发送Type并且模型绑定器将能够实例化正确的实现。