2016-11-04 52 views
1

我有一个ViewModel有一个接口作为属性。当我提交页面时,我得到了“无法创建接口的实例”错误。asp.net MVC模型绑定错误“无法创建接口的实例”

视图模型是这样的:

public class PlanoPagamentoViewModel 
{ 
    //some properties 
    public IPlanoPagamentosParcelas PlanoPagamentosParcelas { get; set; }  
} 

有是实现此接口的两个类。 根据所选的选项,相应的ViewModel将以PartialView方式加载。

public class PlanoPagamentoCartaoViewModel : IPlanoPagamentosParcelas 
{ 
    //some properties 
} 

public class PlanoPagamentoCrediarioViewModel : IPlanoPagamentosParcelas 
{ 
    //some properties 
} 

我做了一个研究,我发现需要创建一个自定义模型绑定的,而我做了:

public class PlanoPagamentoParcelasBinder : DefaultModelBinder 
{ 
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
    { 
     var type = typeof(PlanoPagamentoCartaoViewModel); 
     var model = Activator.CreateInstance(type); 
     bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type); 

     return model; 
    } 
} 

并添加此新的自定义在Global.asax中,Application_Start方法中结合:

ModelBinders.Binders.Add(typeof(IPlanoPagamentosParcelas), new PlanoPagamentoParcelasBinder()); 

它非常适用PlanoPagamentoCartaoViewModel,但我需要另一个自定义绑定为PlanoPagamentoCrediarioViewModel,但我不能只是增加一个新的ModelBinders.Binders.Add与相同的密钥(IPlanoPagamentosParcelas),因为此类型已有一个密钥。

那么,是否有任何其他方法来创建实现相同接口的ViewModel的自定义模型绑定?

+0

将typeof(IPlanoPagamentosParcelas)更改为typeof(PlanoPagamentoParcelasBinder)或typeof(DefaultModelBinder) –

+0

@viveknuna我得到了同样的错误。 我需要添加IPlanoPagamentosParcelas两个的ViewModels,但因为它是一本字典 – Maturano

回答

2

我认为,解决办法是如下:

protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
{ 
     var typeValueProvider = bindingContext.ValueProvider.GetValue("Type"); 

     var type = (int)typeValueProvider.ConvertTo(typeof(int)); 

     Type instanceType = null; 

     switch (type) 
     { 
      case 1: 
       instanceType = typeof(PlanoPagamentoCartaoViewModel); 
       break; 

      case 2: 
       instanceType = typeof(PlanoPagamentoCrediarioViewModel); 
       break; 
     } 

     if (!typeof(IPlanoPagamentosParcelas).IsAssignableFrom(instanceType)) 
     { 
      throw new InvalidOperationException("Bad Type"); 
     } 
     var model = Activator.CreateInstance(instanceType); 
     bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, instanceType); 
     return model; 
} 

在上面的代码var typeValueProvider = bindingContext.ValueProvider.GetValue("Type");Type会是这样来决定实例化的具体类。我已经测试过这个解决方案,它对我很有用。它可以被改进为更具可扩展性(可能会删除开关),但我只是试图让它工作。

这是从我根据我的问题(也许你可以从那里了解更多信息)Polymorphic model binding以下是我创建模拟这种情况下的代码(只是在怀疑的情况下):

机型:

public class NewVehicleViewModel 
{ 
    public string Type { get; set; } 

    public VehicleViewModel Vehicle { get; set; } 
} 

public interface VehicleViewModel 
{ 
    string Name { get; set; } 
    string Color { get; set; } 
} 

public class CarViewModel : VehicleViewModel 
{ 
    public string Color { get; set; } 

    public string Name { get; set; } 

    public string Brand { get; set; } 
} 

public class TankViewModel : VehicleViewModel 
{ 
    public string Color { get; set; } 

    public string Name { get; set; } 

    public string Weapon { get; set; } 
} 

粘结剂:

public class VehicleBinder : DefaultModelBinder 
{ 
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
    { 
     var typeValueProvider = bindingContext.ValueProvider.GetValue("Type"); 

     var type = (int)typeValueProvider.ConvertTo(typeof(int)); 

     Type instanceType = null; 

     switch (type) 
     { 
      case 1: 
       instanceType = typeof(CarViewModel); 
       break; 

      case 2: 
       instanceType = typeof(TankViewModel); 
       break; 
     } 

     if (!typeof(VehicleViewModel).IsAssignableFrom(instanceType)) 
     { 
      throw new InvalidOperationException("Bad Type"); 
     } 
     var model = Activator.CreateInstance(instanceType); 
     bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, instanceType); 
     return model; 
    } 
} 

CONTROLLER:

public class VehiclesController : Controller 
{ 
    // GET: Vehicles 
    public ActionResult Index() 
    { 
     return View(); 
    } 

    [HttpPost] 
    public ActionResult Create(NewVehicleViewModel vm) 
    { 
     return View(); 
    } 
} 

global.asax中:

protected void Application_Start() 
    { 
     AreaRegistration.RegisterAllAreas(); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     BundleConfig.RegisterBundles(BundleTable.Bundles); 
     ModelBinders.Binders.Add(typeof(VehicleViewModel), new VehicleBinder()); 
    } 

VIEW(Index.cshtml):

@{ 
    ViewBag.Title = "New Vehicle"; 
} 


@using (Html.BeginForm("Create", "Vehicles")) 
{ 
    <label>Type</label> 
    <input name="type" type="text" /> 

    <label >Name</label> 
    <input name="Vehicle.Name" type="text"/> 

    <label>Color</label> 
    <input name="Vehicle.Color" type="text" /> 

    <label>Weapon</label> 
    <input name="Vehicle.Weapon" type="text" /> 

    <label>Brand</label> 
    <input name="Vehicle.Brand" type="text" /> 

    <input type="submit" /> 
} 

问候。

+0

非常有趣的方法是不可能的,但我在得到NullRecerenceException: VAR typeValueProvider = bindingContext.ValueProvider.GetValue(“类型”); 我很抱歉我的新手问题,但我需要改变“类型”的东西吗? – Maturano

+0

我也尝试将“Type”更改为bindingContext.ModelName,但它也返回null – Maturano

+0

Type只是一个使用的属性,因此可以决定实例化哪个类。它可能是其他的东西,但你必须决定,不知何种具体的类你将实例化。您可以使用布尔属性,可能是'IsPlanoPagamentoCartao',将它作为隐藏字段放在视图中,并在模型绑定器中使用此属性'bindingContext.ValueProvider.GetValue(“IsPlanoPagamentoCartao”);'来决定创建哪个实例。 – dime2lo