2016-11-04 3 views
1

I 인터페이스를 속성으로 가진 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)가 이미이 유형의 키가 하나 있기 때문입니다.

그래서 동일한 인터페이스를 구현하는 ViewModels에 대한 맞춤 모델 바인딩을 만드는 다른 방법이 있습니까? 위의 코드 var typeValueProvider = bindingContext.ValueProvider.GetValue("Type");Type에서

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; 
} 

는 인스턴스화 콘크리트하는 클래스를 결정하는 것이 될 것이다 :

+0

typeof (IPlanoPagamentosParcelas)를 typeof (PlanoPagamentoParcelasBinder) 또는 typeof (DefaultModelBinder)로 변경하십시오. –

+0

@viveknuna 동일한 오류가 발생했습니다. 두 개의 ViewModels에 IPlanoPagamentosParcelas를 추가해야하지만, 사전이므로 – Maturano

답변

2

나는이 솔루션은 다음과 같다 있다고 생각합니다. 나는이 솔루션을 테스트했고 그것은 나를 위해 일했다. 그것은 좀 더 확장 가능하도록 개선 될 수 있지만 (아마도 다른 무언가를 위해 스위치를 제거하는 것), 나는 그것을 효과있게 만들려고 노력했습니다.

모델 : 여기

내가 (어쩌면 당신이 거기에서 더 많은 정보를 얻을 수 있습니다) 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; } 
} 

BINDER :

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 ("Type"); 초보자 용 질문에 대해 유감 스럽지만 "유형"을 다른 것으로 변경해야합니까? – Maturano

+0

또한 "Type"을 bindingContext.ModelName으로 변경하려고했지만 null도 반환했습니다. – Maturano

+0

유형은 사용되는 속성이므로 인스턴스화 할 클래스를 결정할 수 있습니다. 그것은 다른 것일 수도 있지만, 어떻게 구체화 할 것인지를 결정해야합니다. 부울 속성, 어쩌면'IsPlanoPagamentoCartao'를 사용하여 뷰에서 숨겨진 필드로 배치하고 모델 바인더에서이 속성'bindingContext.ValueProvider.GetValue ("IsPlanoPagamentoCartao");를 사용하여 생성 할 instante를 결정할 수 있습니다. – dime2lo