asp.net mvc - pages - Mejores prácticas de ViewModel
razor pages or mvc (11)
Aquí hay un fragmento de código de mis mejores prácticas:
public class UserController : Controller
{
private readonly IUserService userService;
private readonly IBuilder<User, UserCreateInput> createBuilder;
private readonly IBuilder<User, UserEditInput> editBuilder;
public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
{
this.userService = userService;
this.editBuilder = editBuilder;
this.createBuilder = createBuilder;
}
public ActionResult Index(int? page)
{
return View(userService.GetPage(page ?? 1, 5));
}
public ActionResult Create()
{
return View(createBuilder.BuildInput(new User()));
}
[HttpPost]
public ActionResult Create(UserCreateInput input)
{
if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");
if (!ModelState.IsValid)
return View(createBuilder.RebuildInput(input));
userService.Create(createBuilder.BuilEntity(input));
return RedirectToAction("Index");
}
public ActionResult Edit(long id)
{
return View(editBuilder.BuildInput(userService.GetFull(id)));
}
[HttpPost]
public ActionResult Edit(UserEditInput input)
{
if (!ModelState.IsValid)
return View(editBuilder.RebuildInput(input));
userService.Save(editBuilder.BuilEntity(input));
return RedirectToAction("Index");
}
}
A partir de esta pregunta , parece que tiene sentido que un controlador cree un ViewModel que refleje con mayor precisión el modelo que la vista está tratando de mostrar, pero tengo curiosidad acerca de algunas de las convenciones (soy nuevo en el patrón MVC , si ya no era obvio).
Básicamente, tuve las siguientes preguntas:
- Normalmente me gusta tener una clase / archivo. ¿Tiene sentido esto con un ViewModel si solo se está creando para transferir datos de un controlador a una vista?
- Si un ViewModel pertenece a su propio archivo y está usando una estructura de directorio / proyecto para mantener las cosas separadas, ¿a dónde pertenece el archivo ViewModel ? ¿En el directorio de controladores ?
Eso es básicamente por ahora. Puede que tenga algunas preguntas más por venir, pero esto me ha estado molestando durante la última hora o así, y parece que puedo encontrar orientación consistente en otra parte.
EDITAR: Al observar la aplicación NerdDinner de ejemplo en CodePlex, parece que los ViewModels son parte de los Controllers , pero aún me hace sentir incómodo que no estén en sus propios archivos.
Código en el controlador:
[HttpGet]
public ActionResult EntryEdit(int? entryId)
{
ViewData["BodyClass"] = "page-entryEdit";
EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
return View(viewMode);
}
[HttpPost]
public ActionResult EntryEdit(Entry entry)
{
ViewData["BodyClass"] = "page-entryEdit";
#region save
if (ModelState.IsValid)
{
if (EntryManager.Update(entry) == 1)
{
return RedirectToAction("EntryEditSuccess", "Dictionary");
}
else
{
return RedirectToAction("EntryEditFailed", "Dictionary");
}
}
else
{
EntryEditViewModel viewModel = new EntryEditViewModel(entry);
return View(viewModel);
}
#endregion
}
código en ver modelo:
public class EntryEditViewModel
{
#region Private Variables for Properties
private Entry _entry = new Entry();
private StatusList _statusList = new StatusList();
#endregion
#region Public Properties
public Entry Entry
{
get { return _entry; }
set { _entry = value; }
}
public StatusList StatusList
{
get { return _statusList; }
}
#endregion
#region constructor(s)
/// <summary>
/// for Get action
/// </summary>
/// <param name="entryId"></param>
public EntryEditViewModel(int? entryId)
{
this.Entry = EntryManager.GetDetail(entryId.Value);
}
/// <summary>
/// for Post action
/// </summary>
/// <param name="entry"></param>
public EntryEditViewModel(Entry entry)
{
this.Entry = entry;
}
#endregion
}
proyectos:
DevJet.Web (el proyecto web ASP.NET MVC)
DevJet.Web.App.Dictionary (un proyecto separado de la Biblioteca de clases)
en este proyecto, hice algunas carpetas como: DAL, BLL, BO, VM (carpeta para ver modelos)
Cree una clase base de modelo de vista que comúnmente requiera propiedades como resultado de la operación y datos contextuales, también puede colocar los datos y roles actuales del usuario
class ViewModelBase
{
public bool HasError {get;set;}
public string ErrorMessage {get;set;}
public List<string> UserRoles{get;set;}
}
En la clase de controlador base tiene un método como PopulateViewModelBase (), este método llenará los datos contextuales y los roles de usuario. HasError y ErrorMessage, establecen estas propiedades si hay una excepción al extraer datos de service / db. Enlazar estas propiedades en la vista para mostrar el error. Los roles de usuario pueden utilizarse para mostrar la sección de ocultación en la vista basada en roles.
Para completar los modelos de vista en diferentes acciones de obtención, se puede hacer que sea coherente al tener un controlador base con el método abstracto FillModel
class BaseController :BaseController
{
public PopulateViewModelBase(ViewModelBase model)
{
//fill up common data.
}
abstract ViewModelBase FillModel();
}
En los controladores
class MyController :Controller
{
public ActionResult Index()
{
return View(FillModel());
}
ViewModelBase FillModel()
{
ViewModelBase model=;
string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString();
try
{
switch(currentAction)
{
case "Index":
model= GetCustomerData();
break;
// fill model logic for other actions
}
}
catch(Exception ex)
{
model.HasError=true;
model.ErrorMessage=ex.Message;
}
//fill common properties
base.PopulateViewModelBase(model);
return model;
}
}
Creo lo que llamo un "ViewModel" para cada vista. Los puse en una carpeta llamada ViewModels en mi proyecto web MVC. Los nombro después del controlador y la acción (o vista) que representan. Entonces, si necesito pasar datos a la vista de inicio de sesión en el controlador de membresía, creo una clase MembershipSignUpViewModel.cs y la coloco en la carpeta ViewModels.
Luego agrego las propiedades y métodos necesarios para facilitar la transferencia de datos desde el controlador a la vista. Utilizo el Automapper para pasar de mi ViewModel al Modelo de Dominio y viceversa si es necesario.
Esto también funciona bien para los ViewModels compuestos que contienen propiedades que son del tipo de otros ViewModels. Por ejemplo, si tiene 5 widgets en la página de índice en el controlador de membresía y creó un ViewModel para cada vista parcial, ¿cómo pasa los datos de la acción de Índice a los parciales? Agregue una propiedad a MembershipIndexViewModel de tipo MyPartialViewModel y al renderizar el parcial que pasaría en Model.MyPartialViewModel.
Hacerlo de esta manera le permite ajustar las propiedades parciales de ViewModel sin tener que cambiar la vista del Índice. Todavía pasa en Model.MyPartialViewModel, por lo que hay menos posibilidades de que tenga que pasar por toda la cadena de parciales para arreglar algo cuando todo lo que está haciendo es agregar una propiedad al ViewModel parcial.
También agregaré el espacio de nombres "MyProject.Web.ViewModels" a web.config para que pueda hacer referencia a ellos en cualquier vista sin agregar una declaración de importación explícita en cada vista. Simplemente lo hace un poco más limpio.
En nuestro caso, tenemos los Modelos junto con los Controladores en un proyecto separado de las Vistas.
Como regla general, hemos intentado mover y evitar la mayoría de las cosas de ViewData ["..."] a ViewModel, por lo que evitamos los lanzamientos y las cuerdas mágicas, lo cual es bueno.
El ViewModel también contiene algunas propiedades comunes como la información de paginación para listas o información de encabezado de la página para dibujar migas de pan y títulos. En este momento, la clase base contiene demasiada información en mi opinión y podemos dividirla en tres partes, la información más básica y necesaria para el 99% de las páginas en un modelo de vista base, y luego un modelo para las listas y un modelo. para los formularios que contienen datos específicos para esos escenarios y se heredan de la base.
Finalmente, implementamos un modelo de vista para cada entidad para tratar con la información específica.
La separación de clases por categoría (controladores, modelos de vista, filtros, etc.) no tiene sentido.
Si desea escribir código para la sección de Inicio de su sitio web (/), cree una carpeta llamada Inicio y coloque allí el Controlador de Inicio, IndexViewModel, AboutViewModel, etc. y todas las clases relacionadas utilizadas por las acciones de Inicio.
Si tiene clases compartidas, como un ApplicationController, puede ponerlo en la raíz de su proyecto.
¿Por qué separar las cosas que están relacionadas (HomeController, IndexViewModel) y mantener las cosas juntas que no tienen ninguna relación (HomeController, AccountController)?
Escribí una publicación de blog sobre este tema.
Lanzamos todos nuestros ViewModels en la carpeta Modelos (toda nuestra lógica de negocios está en un proyecto ServiceLayer separado)
Mantengo mis clases de aplicación en una subcarpeta llamada "Core" (o una biblioteca de clases separada) y utilizo los mismos métodos que la aplicación de ejemplo KIGG pero con algunos cambios leves para hacer que mis aplicaciones sean más SECAS.
Creo una clase BaseViewData en / Core / ViewData / donde almaceno las propiedades comunes del sitio.
Después de esto, también creo todas mis clases de ViewData de vista en la misma carpeta que luego se derivan de BaseViewData y tienen propiedades específicas de vista.
Luego creo un ApplicationController del que derivan todos mis controladores. El ApplicationController tiene un método GetViewData genérico de la siguiente manera:
protected T GetViewData<T>() where T : BaseViewData, new()
{
var viewData = new T
{
Property1 = "value1",
Property2 = this.Method() // in the ApplicationController
};
return viewData;
}
Finalmente, en la acción de mi Controlador, hago lo siguiente para construir mi Modelo ViewData
public ActionResult Index(int? id)
{
var viewData = this.GetViewData<PageViewData>();
viewData.Page = this.DataContext.getPage(id); // ApplicationController
ViewData.Model = viewData;
return View();
}
Creo que esto funciona muy bien y mantiene tus vistas ordenadas y tus controladores delgados.
No hay un buen lugar para mantener a sus modelos. Puede guardarlos en un ensamblaje separado si el proyecto es grande y hay muchos ViewModels (Objetos de transferencia de datos). También puede guardarlos en la carpeta separada del proyecto del sitio. Por ejemplo, en Oxite se colocan en el proyecto Oxite que también contiene muchas clases diferentes. Los controladores en Oxite se mueven a un proyecto separado y las vistas también están en un proyecto separado.
En CodeCampServer ViewModels se denominan * Formulario y se colocan en el proyecto de UI en la carpeta Modelos.
En el proyecto MvcPress , se colocan en el proyecto de datos, que también contiene todo el código para trabajar con la base de datos y un poco más (pero no recomendé este enfoque, es solo para una muestra)
Como pueden ver hay muchos puntos de vista. Generalmente mantengo mis ViewModels (objetos DTO) en el proyecto del sitio. Pero cuando tengo más de 10 modelos, prefiero moverlos al ensamblaje separado. Por lo general, en este caso también muevo los controladores al ensamblaje separado.
Otra pregunta es cómo asignar fácilmente todos los datos del modelo a su ViewModel. Sugiero echar un vistazo a la biblioteca de AutoMapper . Me gusta mucho, hace todo el trabajo sucio por mí.
Y también sugiero mirar el proyecto SharpArchitecture . Proporciona una muy buena arquitectura para proyectos y contiene muchos marcos y guías geniales y una gran comunidad.
Personalmente sugeriría que si el ViewModel es algo menos trivial, use una clase separada.
Si tiene más de un modelo de vista, sugiero que tenga sentido particionarlo en al menos un directorio. Si el modelo de vista se comparte más tarde, el espacio de nombre implícito en el directorio facilita el traslado a un nuevo ensamblaje.
Una clase ViewModel está ahí para encapsular múltiples piezas de datos representados por instancias de clases en un objeto fácil de administrar que puede pasar a su Vista.
Tendría sentido tener sus clases de ViewModel en sus propios archivos, en el propio directorio. En mis proyectos tengo una subcarpeta de la carpeta de modelos llamada ViewModels. Ahí es donde viven mis ViewModels (por ejemplo, ProductViewModel.cs
).