Domanda

Se ho la seguente vista fortemente:

<%@ 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 di tipo INVIARE:

[HttpPost]
public ActionResult Index(Location model)

Ricevo un errore di runtime affermando "Impossibile creare una classe astratta

Quale ovviamente ha senso. Tuttavia, non sono sicuro di quale sia la soluzione migliore qui.

Ho molti tipi concreti (circa 8), e questa è una vista in cui è possibile modificare solo le proprietà della classe astratta.

Quello che ho provato Fare è creare sovraccarichi per tutti i diversi tipi di cemento 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);
}

ecc. ecc

Poi:

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

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

Cosa facciamo? Non possiamo semplicemente legarsi a un modello astratto?

È stato utile?

Soluzione

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 un po 'di ulteriore parametro per indicare quale classe concreta si ha bisogno. Altrimenti avrei attenersi a modelli vista concreti come parametri di azione.

Altri suggerimenti

Si può anche costruire un ModelBinder generico che funziona per tutti i modelli astratti. La mia soluzione richiede di aggiungere un campo nascosto alla vista chiamata ' ModelTypeName ' con il valore impostato al nome del tipo di cemento che si desidera. Tuttavia, dovrebbe essere possibile per rendere questa cosa più intelligente e scegliere un tipo concreto abbinando proprietà del tipo a campi nella vista.

Global.asax.cs nella 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);
    }
}

Giusto per buttare là fuori - Sono molto interessato a ciò che altri potrebbero rispondere, ma questo è quello che ho finito per fare 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, TryUpdateModel poi utilizzati da lì.

Sembrava ci potrebbe essere un modo migliore, ma non avevo mai ottenuto intorno ad pensarci più.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top