tutorial standard net inicializar imapper example asp.net-mvc drop-down-menu viewmodel automapper

asp.net-mvc - standard - inicializar automapper c#



Cómo obtener datos para una lista desplegable en el modelo de vista al usar AutoMapper/AutoMapViewResult (4)

Después de leer ASP.NET MVC 2 en Acción y ver la presentación de Jimmy Bogard de MvcConf (¡ambas muy recomendables!), Comencé a implementar algunas de sus ideas.

Una de las mejores cosas que hacen, no es solo usar AutoMapper para mapear sus entidades a algún modelo de vista, sino automatizar esto con un AutoMapViewResult:

public class EventsController : BaseController { public ActionResult Show(Event id) // EntityModelBinder gets Event from repository { return AutoMapView<EventsShowModel>(id); // AutoMapView<T>(model) is a helper method on the BaseController, that calls AutoMapViewResult<T>(...) } } // not exactly what you''ll find in the book, but it also works :-) public class AutoMapViewResult<TDestination> : ViewResult { public AutoMapViewResult(string viewName, string masterName, object model) { ViewName = viewName; MasterName = masterName; ViewData.Model = Mapper.Map(model, model.GetType(), typeof(TDestination)); } }

Todo esto funciona muy bien, pero ahora hay una acción de Edit con su EventsEditModel :

public class EventsEditModel { // ... some properties ... public int LocationId { get; set; } public IList<SelectListItem> Locations { get; set; } } public class EventsController : BaseController { public ActionResult Edit(Event id) { return AutoMapView<EventsEditModel>(id); } }

Y ahora (finalmente) la pregunta:

¿Cuál cree usted que es la mejor forma de obtener las ubicaciones de algún tipo de fuente de datos, como un repositorio de la propiedad Locations EventsEditModel ?

Tenga en cuenta que quiero usar AutoMapViewResult y muchas combinaciones diferentes de entidades-modelos de vistas.

Actualizar:

Fui con la idea de Necros y creé un atributo personalizado. Puede mirar el código y descargarlo en mi blog ASP.NET MVC: cargar datos para listas de selección en el modelo de edición utilizando atributos .


Bien, agregue un constructor con un parámetro y una propiedad en su controlador y use DI (personalmente me gusta Ninject) para inyectar la implementación correcta del repositorio:

public IEventsRepository _repo; public EventsController(IEventsRepository repository) { _repo = repository; }

Conecte (enlace) las dependencias en global.asax.cs en la aplicación Ninject y el módulo del sitio (si necesita una respuesta expandida con la incluida, por favor, avíseme),

luego, en su acción Editar, use el repositorio para obtener las Locations . Supongamos que tiene el método LoadLocations() en su interfaz de depósito y su implementación concreta en, por ejemplo, SqlEventsRepository (implementa IEventsRepository ), lo hace simplemente llamando al método:

public ActionResult Edit(Event id) { ... EventsEditModel model = new EventsEditModel(); _repo.GetInstance(id); model.Locations = _repo.LoadLocations(); ... }

Lo estoy inventando porque no has proporcionado demasiada información. Y no conozco los detalles de Automapper cuando desea cargar algunos datos adicionales del almacén de datos antes de asignar la entidad al ViewModel.

Tampoco se especifica si esta acción de edición es GET o POST , pero supongo que es GET . Suponiendo que realmente se GET , no sé cómo se puede cargar nada proporcionando a la Entidad la acción.

Lo más común es que los métodos GET usen parámetros de tipo cadena o int (lo más probable es que sean babosas o identificadores de algún tipo) y POST methos usan parámetros de tipo ViewModel (no Entity).

Entonces la firma del método POST debería ser así

[HttpPost] public ActionResult Edit(EventsEditModel model)...

Solía ​​usar Entidades directamente en mis firmas de acción y estaba fallando todo el tiempo, por lo que ahora lo desanimo a otros.

HTH


Le recomendaría que mire la asp.net-mvc sample application desde aquí, que hace esto mucho más simple que el automaper

Mire especialmente el TinyController


No he llegado al punto (desde que vi la charla) cuando lo necesitaba, pero tengo una posible solución para esto. Creo que funcionaría crear un atributo, especificando que esta propiedad necesita ser cargada. Comenzaría con una clase abstracta:

public abstract class LoadDataAttribute : Attribute { public Type Type { get; set; } protected LoadDataAttribute(Type type) { Type = type; } public abstract object LoadData(); }

A continuación, cree una versión específica para cada tipo que desee cargar (ubicaciones en su caso)

public class LoadLocationsAttribute : LoadDataAttribute { public LoadLocationsAttribute() : base(typeof(IList<SelectListItem>)) public override object LoadData() { // get locations and return IList<SelectListItem> } }

En su ExecuteResult de AutoMappViewResult , encontrará todas las propiedades con LoadDataAttribute , invocar LoadData() , convertirlo al tipo especificado en el atributo y asignarlo a la propiedad.

En caso de que solo quiera cargar listas de selección de esta manera, puede simplemente devolver IList<SelectListItem> lugar de object , y ahorrarse algunos problemas con el casting.

Su modelo de vista obviamente usaría el atributo.

public class EventsEditModel { // ... some properties ... public int LocationId { get; set; } [LoadLocations] public IList<SelectListItem> Locations { get; set; } }


Mi solución a esto fue introducir el concepto de enriquecedores de modelos, clases simples que "enriquecen" el modelo antes de pasarlo a la Vista ():

public class SiteSettingsModelEnricher : IModelEnricher<SiteSettingsModel> { private readonly IThemeProvider themeProvider; public SiteSettingsModelEnricher(IThemeProvider themeProvider) { this.themeProvider = themeProvider; } public SiteSettingsModel Enrich(SiteSettingsModel model) { var themes = from t in themeProvider.GetThemes() select new SelectListItem { Text = t, Value = t }; model.Themes = themes; return model; } }

Mi método AutoMapperViewResult ExecuteResult se ve así:

public override void ExecuteResult(ControllerContext context) { var model = Mapper.Map(this.Model, typeof(TSource), typeof(TDestination)) as TDestination; // enrichers var enricher = DependencyResolver.Current.GetService<IModelEnricher<TDestination>>(); if (enricher != null) { model = enricher.Enrich(model); } this.ViewData.Model = model; base.ExecuteResult(context); }

Como también estoy usando el FormActionResult de la presentación de Jimmy, también uso el enriquecedor antes de devolver el resultado de la falla. Esto significa que cosas como listas de selección se reenganchan y mantienen las cosas súper SECAS.

[Actualizar]

Publiqué una solución mejorada aquí que se basa en lo anterior.