2010-10-25 12 views
11

se ho la seguente vista fortemente tipizzato:ASP.NET MVC 2 - il legame con Estratto modello

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<XXX.DomainModel.Core.Locations.Location>" %> 

Dove posizione è una classe astratta.

e ho il seguente controller, che accetta un modello fortemente tipizzato tramite una POST:

[HttpPost] 
public ActionResult Index(Location model) 

ottengo un errore di runtime che indica "Impossibile creare classe astratta

Quale di Ovviamente, non sono sicuro di quale sia la soluzione migliore:

Ho molti tipi di calcestruzzo (circa 8), e questa è una vista in cui c una sola proprietà di modifica della classe astratta.

Quello che ho provato è creare sovraccarichi per tutti i diversi tipi di calcestruzzo ed eseguire la mia logica in un metodo comune.

[HttpPost] 
public ActionResult Index(City model) 
{ 
    UpdateLocationModel(model); 
    return View(model); 
} 

[HttpPost] 
public ActionResult Index(State model) 
{ 
    UpdateLocationModel(model); 
    return View(model); 
} 

etc etc

E poi:

[NonAction] 
private void UpdateLocationModel (Location model) 
{ 
    // ..snip - update model 
} 

Ma questo non funziona neanche, MVC lamenta i metodi di azione sono ambigui (anche senso).

Cosa facciamo? Possiamo semplicemente non legarci a un modello astratto?

+1

Buona domanda. Interessato a vedere le risposte! –

+0

Sono curioso di sapere se hai mai trovato un modo migliore di gestirlo? –

+0

@Mystere Man - no. Non ho dovuto farlo di nuovo. Se lo facessi, farei ciò che la risposta accettata suggerisce. – RPM1984

risposta

7

Come di scrivere un modello personalizzato legante per questa classe astratta:

public class CustomBinder : DefaultModelBinder 
{ 
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
    { 
     // TODO: based on some request parameter choose the proper child type 
     // to instantiate here 
     return new Child(); 
    } 
} 

questo ha senso solo se si dispone di un modulo in cui elementi di input vengono inseriti dinamicamente in base qualche azione dell'utente. In questo caso è necessario passare alcuni parametri aggiuntivi per indicare quale classe concreta è necessaria. In caso contrario, mi attenerei ai modelli di vista concreti come parametri di azione.

+0

Bene, questa è una vista molto semplice, modifica i campi per le proprietà sul modello astratto. Quindi non volevo "duplicare" questo codice HTML su più viste fortemente tipizzate. Analizzerò i raccoglitori di modelli personalizzati, poiché conosco il tipo di figlio quando eseguo il rendering della vista. Lo proverò domani in ufficio - grazie. – RPM1984

+0

Okay, ho letto dei raccoglitori di modelli e sono d'accordo: non ha senso nel mio scenario. Ho intenzione di rimanere con i modelli di vista concreti. Questo funzionerebbe comunque, quindi accetterò la tua risposta. Grazie. – RPM1984

1

Giusto per buttarlo là fuori - Sono molto interessato a ciò che gli altri potrebbero rispondere, ma questo è quello che ho fatto nel caso in cui ho avuto una situazione simile;

In sostanza, non ho usato la classe del modello come parametro nel metodo d'azione, invece passando FormCollection e testare un paio di noti discriminatori di capire quale tipo di creare/modificare, poi utilizzato TryUpdateModel da lì.

Sembrava che ci potesse essere un modo migliore, ma non mi ero mai preoccupato di pensarci di più.

+0

Sì, quello era il mio prossimo passo! :) Ma awww, non voglio la bontà fortemente tipizzata. :) – RPM1984

+0

È delizioso e nutriente; Parte di questa colazione completa! –

3

È anche possibile creare un ModelBinder generico che funzioni per tutti i modelli astratti. La mia soluzione richiede di aggiungere un campo nascosto alla vista chiamata "ModelTypeName" con il valore impostato sul nome del tipo di calcestruzzo che si desidera. Tuttavia, dovrebbe essere possibile rendere questa cosa più intelligente e scegliere un tipo concreto associando le proprietà del tipo ai campi nella vista.

Nella tua Global.asax.cs file in Application_Start():

ModelBinders.Binders.DefaultBinder = new CustomModelBinder(); 

CustomModelBinder:

public class CustomModelBinder2 : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var modelType = bindingContext.ModelType; 
     if (modelType.IsAbstract) 
     { 
      var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ModelTypeName"); 
      if (modelTypeValue == null) 
       throw new Exception("View does not contain ModelTypeName"); 

      var modelTypeName = modelTypeValue.AttemptedValue; 

      var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName); 

      if (type != null) 
      { 
       var instance= bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type); 
       bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type); 
      } 
     } 
     return base.BindModel(controllerContext, bindingContext); 
    } 
} 
+0

Questa è una risposta utile, ma solo per chiunque la applichi, ho il sospetto che qui potrebbe esserci un rischio per la sicurezza se il client restituisce un nome di tipo inaspettato ma legittimo con un costruttore dannoso. Destra? –

+0

vedere anche http://stackoverflow.com/questions/7222533/polymorphic-model-binding/9813472#9813472 –

+0

@ uosɐs: solo nel caso MVC sta tentando di mappare in una sottoclasse (vedere '.IsSubclassOf (...) ') di una classe astratta prevista utilizzata nella firma dell'azione del controller o una delle proprietà figlio degli oggetti della firma dell'azione del controllore. E poi deve avere un costruttore "dannoso". Sto pensando di usare questo codice. Puoi dare un esempio di tale rischio per la sicurezza? –

Problemi correlati