Pregunta

De esta pregunta , parece que tiene sentido tener un el controlador crea un ViewModel que refleja con mayor precisión el modelo que la vista está tratando de mostrar, pero tengo curiosidad por algunas de las convenciones (soy nuevo en el patrón MVC, si no fuera así ya obvio).

Básicamente, tuve las siguientes preguntas:

  1. Normalmente me gusta tener una clase / archivo. ¿Tiene sentido con un ViewModel si solo se está creando para transferir datos de un controlador a una vista?
  2. Si un ViewModel pertenece a su propio archivo, y está utilizando una estructura de directorio / proyecto para mantener las cosas separadas, ¿a dónde pertenece el archivo ViewModel ? En el directorio Controladores ?

Eso es básicamente todo por ahora. Es posible que tenga algunas preguntas más por venir, pero esto me ha estado molestando durante la última hora más o menos, y parece que puedo encontrar una guía constante en otros lugares.

EDITAR: Mirando la muestra aplicación NerdDinner en CodePlex, parece que los ViewModels son parte de Controladores , pero todavía me incomoda que no estén en sus propios archivos.

¿Fue útil?

Solución

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 Registrarse en el controlador de Membresía, creo una clase MembershipSignUpViewModel.cs y la pongo 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 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 Index a los parciales? Agregue una propiedad al MembershipIndexViewModel de tipo MyPartialViewModel y al representar el parcial pasaría en Model.MyPartialViewModel.

Hacerlo de esta manera le permite ajustar las propiedades parciales de ViewModel sin tener que cambiar la vista de í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 permitirme hacer referencia a ellos en cualquier vista sin tener que agregar una declaración de importación explícita en cada vista. Solo lo hace un poco más limpio.

Otros consejos

Separar clases por categoría (Controladores, ViewModels, Filtros, etc.) no tiene sentido.

Si desea escribir código para la sección Inicio de su sitio web (/), cree una carpeta llamada Inicio y coloque allí el HomeController, IndexViewModel, AboutViewModel, etc. y todas las clases relacionadas utilizadas por las acciones de Inicio.

Si ha compartido clases, como un ApplicationController, puede colocarlo 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.

Mantengo mis clases de aplicación en una subcarpeta llamada " Core " (o una biblioteca de clases separada) y utilice los mismos métodos que la aplicación de muestra KIGG pero con algunos pequeños cambios en hacer que mis aplicaciones sean más SECAS.

Creo una clase BaseViewData en / Core / ViewData / donde almaceno propiedades comunes de todo el sitio.

Después de esto, también creo todas mis clases 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. ApplicationController tiene un método genérico GetViewData 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 mi acción Controlador, hago lo siguiente para construir mi Modelo de 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 sus vistas ordenadas y sus controladores delgados.

Una clase ViewModel está ahí para encapsular múltiples 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 ViewModel en sus propios archivos, en el propio directorio. En mis proyectos tengo una subcarpeta de la carpeta Modelos llamada ViewModels. Ahí es donde viven mis ViewModels (por ejemplo, ProductViewModel.cs).

No hay un buen lugar para guardar sus modelos. Puede mantenerlos en un ensamblaje separado si el proyecto es grande y hay muchos ViewModels (Objetos de transferencia de datos). También puede guardarlos en una 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 proyectos separados y las vistas también están en proyectos separados.
En CodeCampServer los ViewModels se denominan * Formulario y se colocan en el proyecto de IU en la carpeta Modelos.
En el 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)
Entonces puedes ver que hay muchos puntos de vista. Por lo general, mantengo mis ViewModels (objetos DTO) en el proyecto del sitio. Pero cuando tengo más de 10 modelos, prefiero moverlos para ensamblarlos por separado. Por lo general, en este caso también estoy moviendo controladores para separar el ensamblaje.
Otra pregunta es cómo asignar fácilmente todos los datos del modelo a su ViewModel. Sugiero echar un vistazo a la biblioteca AutoMapper . Me gusta mucho, hace todo el trabajo sucio para mí.
Y también sugiero mirar el SharpArchitecture proyecto. Proporciona una arquitectura muy buena para proyectos y contiene muchos marcos y guías geniales y una gran comunidad.

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

Lanzamos todos nuestros ViewModels en la carpeta Modelos (toda nuestra lógica de negocios está en un proyecto de ServiceLayer separado)

Personalmente sugeriría si ViewModel es cualquier cosa menos trivial y luego usar una clase separada.

Si tiene más de un modelo de vista, le sugiero que tenga sentido dividirlo 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 ensamblado.

En nuestro caso, tenemos los Modelos junto con los Controladores en un proyecto separado de las Vistas.

Como regla general, hemos tratado de movernos y evitar la mayoría de las cosas de ViewData [" ... "] al ViewModel, por lo tanto, evitamos fundiciones y cadenas mágicas, que es una buena cosa.

El ViewModel también posee algunas propiedades comunes como la información de paginación para listas o información de encabezado de la página para dibujar rutas de navegación 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 heredan de la base.

Finalmente, implementamos un modelo de vista para cada entidad para tratar la información específica.

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 el modelo de vista:

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 (la web ASP.NET MVC proyecto)

  • DevJet.Web.App.Dictionary (a 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 tenga propiedades requeridas como el resultado de la operación y los 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, tenga un método como PopulateViewModelBase (), este método completará los datos contextuales y los roles de usuario. HasError y ErrorMessage, configure estas propiedades si hay una excepción al extraer datos del servicio / db. Enlace estas propiedades a la vista para mostrar el error. Los roles de usuario se pueden usar para mostrar la sección oculta en la vista en función de los roles.

Para rellenar modelos de vista en diferentes acciones de obtención, puede hacerse 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 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;
}
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top