ASP.NET MVC 3 - 将模型绑定到List<>包含不同类型

时间:2012-04-29 19:46:20

标签: asp.net-mvc-3

我正在尝试研究如何将包含多个对象类型列表的模型绑定到ASP.NET MVC 3中的回发操作。我有以下类来定义车辆类型的列表:< / p>

public enum VehicleType
{
    Car,
    Plane.
    Boat
}

public class BaseVehicle
{
    public VehicleType VehicleType { get; set; }
    public string Name { get; set; }
    public int Passengers { get; set; }
}

public class Plane : BaseVehicle
{
    public int WingSpan { get; set; }
    // -- etc --
}

// Properties omitted
public class Car : BaseVehicle {}
public class Boat : BaseVehicle {}

public class VehiclesViewModel
{
    public string Notes { get; set; }
    public List<BaseVehicle> Vehicles { get; set; }
}

以上类别由以下视图显示:

<!-- VehiclesView.cshtml - loaded by the controller -->
@model Mvc3Test.Models.VehiclesViewModel
<h2>Vehicles</h2>
@Html.EditorFor(m => m.Notes)
<hr />
@Html.EditorFor(m => m.Vehicles)

<!-- BaseVehicle.cshtml -->
@model BaseVehicle
@using Mvc3Test.Data
@Html.HiddenFor(m => m.VehicleType)
@{
    if (Model.VehicleType == VehicleType.Car)
    {
        Html.RenderPartial("Car", (Car)Model);
    }
    else if (Model.VehicleType == VehicleType.Plane)
    {
        Html.RenderPartial("Plane", (Plane)Model);
    }
    // etc..
}

<-- Plane.cshtml -->
@model Mvc3Test.Data.Plane
<h2>Plane</h2>
<p>Name: @Html.EditorFor(m => m.Name)</p>
<p>Passengers: @Html.EditorFor(m => m.Passengers)</p>
<p>Wingspan: @Html.EditorFor(m => m.WingSpan) metres</p>

<!-- Car.cshtml omitted -->

我不知道上面是处理显示的最佳方式(尤其是视图中的if语句),但它现在有效。问题是如何绑定回viewmodel类。我已经尝试用Html.TextBox()替换Html.TextBorFor()所以我可以添加绑定前缀(“Vehicles.Car”等...)但似乎没有办法获取默认模型绑定器来确定在html中表示了什么类,因此可以实例化正确的类型。

我想我必须编写一个自定义模型绑定器才能解决它 - 这是正确的使用方法还是我错过了另一种方式?

感谢您的帮助

1 个答案:

答案 0 :(得分:2)

首先

if (Model.VehicleType == VehicleType.Car)
    {
        Html.RenderPartial("Car", (Car)Model);
    }
    else if (Model.VehicleType == VehicleType.Plane)
    {
        Html.RenderPartial("Plane", (Plane)Model);
    }

使用EditorFor.Works可以省略。

至于绑定 - IMO最好的方法是创建自定义模型绑定器。

示例:

public class DerivedTypeModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var typeName = GetVehicleType(bindingContext);
            if (typeName  != null)
            {
                var modelType = Type.GetType(typeName);
                var targetTypeInstance = Activator.CreateInstance(modelType);
                bindingContext = new ModelBindingContext
                                     {
                                         ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => targetTypeInstance, modelType),
                                         ModelState = bindingContext.ModelState,
                                         FallbackToEmptyPrefix = bindingContext.FallbackToEmptyPrefix,
                                         ModelName = bindingContext.FallbackToEmptyPrefix ? string.Empty : bindingContext.ModelName,
                                         ValueProvider = bindingContext.ValueProvider,
                                     };
            }
            return base.BindModel(controllerContext, bindingContext);
        }
        private string GetVehicleType(ModelBindingContext bindingContext)
        {
            var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "." + "VehicleType");
            if (valueResult == null && bindingContext.FallbackToEmptyPrefix)
            {
                valueResult = bindingContext.ValueProvider.GetValue("VehicleType");
            }
            return valueResult == null ? null : valueResult.AttemptedValue;
        }
    }