2016-11-04 1 views
1

Ich habe ein ViewModel, das eine Schnittstelle als Eigenschaft hat. Als ich die Seite einreichte, bekam ich den Fehler "Kann keine Instanz der Schnittstelle erstellen".asp.net MVC-Modell Binding-Fehler "kann keine Instanz der Schnittstelle erstellen"

Das Ansichtsmodell ist wie folgt:

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

sind es zwei Klassen, die diese Schnittstelle implementieren. Die entsprechenden ViewModels werden dynamisch mit einer PartialView geladen, abhängig von der ausgewählten Option.

public class PlanoPagamentoCartaoViewModel : IPlanoPagamentosParcelas 
{ 
    //some properties 
} 

public class PlanoPagamentoCrediarioViewModel : IPlanoPagamentosParcelas 
{ 
    //some properties 
} 

habe ich eine Forschung, und ich fand, dass die Notwendigkeit eines benutzerdefinierten Modells schaffen Bindung, und ich habe das:

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

und fügen Sie diese neue Gewohnheit in Global.asax Bindung, Application_Start Methode:

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

Es funktioniert gut für PlanoPagamentoCartaoViewModel, aber ich würde eine andere kundenspezifische Anbindungen für die PlanoPagamentoCrediarioViewModel haben müssen, aber ich kann eine neue ModelBinders.Binders.Add nicht nur hinzufügen, mit der gleiche Schlüssel (IPlanoPagamentosParcelas), weil es bereits einen Schlüssel mit diesem Typ gibt.

Gibt es also einen anderen Ansatz zum Erstellen einer benutzerdefinierten Modellbindung für ViewModels, die die gleiche Schnittstelle implementieren?

+0

ändern typeof (IPlanoPagamentosParcelas) zu typeof (PlanoPagamentoParcelasBinder) oder typeof (DefaultModelBinder) –

+0

@viveknuna Ich habe den gleichen Fehler. Ich müsste die IPlanoPagamentosParcelas für die beiden ViewModels hinzufügen, aber es ist nicht möglich, da es ein Wörterbuch ist – Maturano

Antwort

2

Ich glaube, dass die Lösung wie folgt lautet:

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

In dem obigen Code var typeValueProvider = bindingContext.ValueProvider.GetValue("Type");Type wäre etwas, das konkrete Klasse zu instanziiert zu entscheiden. Ich habe diese Lösung getestet und es hat für mich funktioniert. Es kann verbessert werden, um erweiterbar zu sein (den Schalter für etwas anderes vielleicht entfernend), aber ich habe gerade versucht, es zum Funktionieren zu bringen. Hier

ist, von wo aus ich anhand meiner Frage (vielleicht können Sie mehr Informationen von dort erhalten) Polymorphic model binding und unten ist der Code, den ich schuf dieses Szenario zu simulieren (nur im Falle von Zweifeln):

MODELLE:

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

REGLER:

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" /> 
} 

Grüße.

+0

sehr interessante Ansatz, aber ich bekomme NullRecerenceException auf: var typeValueProvider = bindingContext.ValueProvider.GetValue ("Typ"); Es tut mir leid für meine neue Frage, aber muss ich den "Typ" für etwas anderes ändern? – Maturano

+0

Ich habe auch versucht, "Type" in bindingContext.ModelName zu ändern, aber es gibt auch Null zurück – Maturano

+0

Type ist nur eine Eigenschaft verwendet, so dass entschieden werden kann, welche Klasse zu instanziieren. Es könnte etwas anderes sein, aber Sie müssen entscheiden, welche konkrete Klasse Sie instanziieren werden. Sie hätten die boolesche Eigenschaft, vielleicht 'IsPlanoPagamentoCartao' verwenden können, sie als verstecktes Feld in der Ansicht platzieren können und im Modellbinder diese Eigenschaft 'bindingContext.ValueProvider.GetValue (" IsPlanoPagamentoCartao ") verwenden, um zu entscheiden, welche Instanz erstellt werden soll. – dime2lo

Verwandte Themen