2016-11-04 53 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