ASP.NET MVC 2 – Enlace a modelo abstracto

Si tengo la siguiente vista fuertemente tipada:

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

Donde la ubicación es una clase abstracta.

Y tengo el siguiente Controlador, que acepta un Modelo fuertemente tipado a través de un POST:

 [HttpPost] public ActionResult Index(Location model) 

Recibo un error en tiempo de ejecución que indica “No se puede crear una clase abstracta

Lo que por supuesto tiene sentido. Sin embargo, no estoy seguro de cuál es la mejor solución aquí.

Tengo muchos tipos concretos (alrededor de 8), y esta es una vista en la que solo puede editar las propiedades de la clase abstracta.

Lo que he intentado hacer es crear sobrecargas para todos los diferentes tipos concretos y realizar mi lógica en un método común.

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

etcétera etcétera

Y entonces:

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

Pero esto tampoco funciona , MVC se queja de que los métodos de acción son ambiguos (también tiene sentido).

qué hacemos? ¿No podemos simplemente unirnos a un modelo abstracto?

¿Qué tal si escribes un cuaderno de modelos personalizado para esta clase abstracta?

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

Esto tiene sentido solo si tiene un formulario donde los elementos de entrada se insertan dinámicamente en función de alguna acción del usuario. En este caso, debe pasar algún parámetro adicional para indicar qué clase concreta necesita. De lo contrario me apegaría a modelos de vista concretos como parámetros de acción.

También puede crear un ModelBinder genérico que funcione para todos sus modelos abstractos. Mi solución requiere que agregue un campo oculto a su vista llamado ‘ ModelTypeName ‘ con el valor establecido en el nombre del tipo concreto que desea. Sin embargo, debería ser posible hacer esto más inteligente y elegir un tipo concreto haciendo coincidir las propiedades de tipo con los campos de la vista.

En su archivo Global.asax.cs en 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); } } 

Solo para lanzarlo por ahí. Estoy muy interesado en lo que otros puedan responder, pero esto es lo que terminé haciendo en el caso de que tuve una situación similar;

Básicamente, no FormCollection la clase modelo como un parámetro en el método de Acción, en lugar de pasar FormCollection y probar un par de discriminadores conocidos para averiguar qué tipo crear / editar, luego usé TryUpdateModel desde allí.

Parecía que podría haber una mejor manera, pero nunca pude pensar más en eso.