2010-11-26 7 views
4

Ich bin auf der Suche nach der Verwendung der UpdateModel-Methode zu einer Sub-Klasse, die zur Laufzeit abgerufen wird, wäre toll, wenn jemand könnte das Licht auf, ob ich einen totalen Hash davon machen und/oder ob oder nicht was ich versuche zu tun ist möglich.MVC UpdateModel und Sub Classes vs Base Class

Ich verwende eine generische Aktion, um die Validierung einer Reihe von Teilansichten zu steuern; Ich versuche, von einer bestimmten Aktion pro Teilansicht wegzukommen.

Jede Teilansicht ein einzigartiges Modell hat, die von einem Basismodell ableitet:

public class ModelA : ModelBase{ 
    [Required] 
    public string SomeStringProperty{get;set;} 
... 
} 
public class ModelB : ModelBase{ 
    [Required] 
    public DateTime? SomeDateProperty{get;set;} 
... 
} 
public class ModelBase{ 
    public Guid InstanceId{get;set;} 
} 

ich die Formcollection auf der Aktion bin mit den übermittelten Formularelemente und ihre Werte zu erhalten, schließt dies die Art des Modells dass die Ansicht verwenden sollte, um ihre Anfrage zu validieren. Ignorieren Sie die Auswirkungen auf die Sicherheit dieses für dieses Beispiel, bin ich von ihnen bewusst, und dies ist eine interne nur proof of concept

[HttpPost] 
    public ActionResult ChangeCaseState(int id, FormCollection formCollection) 
    { 
     Guid instanceId = new Guid(formCollection["instanceId"]); 
     string modelType = formCollection["modelType"]; 
     //Return a specific Model class based on the event/modelType 
     var args = GetStateModelClass(modelType, instanceId); 

     try 
     { 
      UpdateModel(args); 
      if(Model.IsValid){ 
      ... 
     } 
     catch (Exception) 
     { 
      return View("~/Views/Shared/StateForms/" + modelType + ".ascx", args); 
     }... 

Und hier ist der Code, den ich mit einem Sub-Klasse zurückzukehren, basierend auf Der ModelType wurde an den Controller übergeben.

private static ModelBase StateModelClassFactory(string stateModelTypeName, Guid instanceId) 
     { 
      switch (stateModelTypeName) 
      { 
       case "modelTypeA": 
        return new ModelA(workflowInstanceId); 
       case "modelTypeB": 
        return new ModelB(workflowInstanceId); 
    ... 
    } 

Da der Rückgabetyp der StateModelClassFactory Methode der Basisklasse ist, auch wenn ich eine Sub-Klasse tatsächlich bin Rückkehr verwendete Modell Binder durch das Updateverfahren bindet nur gegen die Werte innerhalb der Basisklasse.

Irgendwelche Ideen, wie ich dieses Problem lösen kann?

UPDATE:

ich ein Customer Model Binder erstellt:

public class CustomModelBinder : DefaultModelBinder 
    { 
     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
      { 

und das neue Modell Binder auf die richtige Basisklasse zugewiesen, um zu sehen, was unter der Haube auf ein wenig mehr los war:

Wenn ich den Modellbinder debuggen und den BindingContext überprüfe, representiert die Model-Eigenschaft das Richtige Sub-Klasse, aber die ModelType-Eigenschaft ist die der Basisklasse. Soll ich den ModelType innerhalb der BindModel-Methode ändern? Wenn es irgendwelche Hinweise darauf gibt, wie dies zu tun ist, scheint der Setter auf dem ModelType überflüssig geworden zu sein. Ich habe auch festgestellt, dass die SomeDateProperty von der Sub-Klasse in der PropertyMetadata-Eigenschaft korrekt ist. Scheint so ähnlich zu sein, wie ich möchte.

Antwort

2

Also ich denke, ich habe mein Problem gelöst. Wegen der Art und Weise, wie ich die Model-Klasse vor dem Aufruf des UpdateModels abrufe, bindet das Model Binder die BaseClass, obwohl das Model das der SubClass war - das ist der Code, den ich zur Lösung meines speziellen Problems benutzt habe:

public class SubClassModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var model = bindingContext.Model; 
     var metaDataType = ModelMetadataProviders.Current.GetMetadataForType(null, model.GetType()); 
     bindingContext.ModelMetadata = metaDataType; 
     bindingContext.ModelMetadata.Model = model; 

     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

Und in der Global.asax:

ModelBinders.Binders.Add(typeof(ModelBase), new SubClassModelBinder()); 

Dank Darin für seine inital Zeiger.

+0

+1. Danke für das Teilen Ihrer Lösung. –

0

Um dieses Problem zu lösen, könnten Sie einen benutzerdefinierten Modellordner für den Basistyp schreiben, der basierend auf dem Wert der Zeichenfolgeeigenschaft die richtige untergeordnete Instanz zurückgibt.

+0

Dank Darin, ich hatte ein schnelles Spiel, was ich denke, der Start in Ihre Lösung ist und die Frage aktualisiert. – Tr1stan

5

Ich lief in dieser Frage und stellte fest, dass eine bessere allgemeine Ansatz gerade Ihr Modell dynamic werfen würde, während es in UpdateModel vorbei:

[HttpPost] 
public ActionResult ChangeCaseState(int id, FormCollection formCollection) 
{ 
    ...try 
    { 
     UpdateModel((dynamic)args);//!!notice cast to dynamic here 
     if(Model.IsValid){ 
     ... 
    } 
    catch... 

Diese alle verfügbaren Eigenschaften meiner Art zu setzen erscheint , unabhängig davon, ob meine Variable mit dem Basistyp versehen ist.

Es gibt ein Workitem in CodePlex für dieses Problem eingereicht: http://aspnet.codeplex.com/workitem/8277?ProjectName=aspnet

+0

Danke. Ich habe .net 3.5 (Tag zu der Frage hinzugefügt). Ich bin neu bei dem dynamischen Keyword, da ich noch nicht an allen .net 4.0-Websites gearbeitet habe, werde es versuchen, wenn es immer noch ein Problem ist. – Tr1stan

+0

Nice find, ich stieß auf das gleiche Problem bei der Verwendung von TryUpdateModel anstelle von UpdateModel und Casting auf dynamische tat den Trick. – blins

+0

Heirate mich! Danke, ich war in der Hölle, bevor ich auf diesen Posten stieß. x – ctrlplusb