net - web forms c#
Ver modelos en MVC/MVVM/Separación de capas: ¿mejores prácticas? (2)
La parte divertida: esto no se limita a los viewmodels
de viewmodels
en MVC
, en realidad es una cuestión de separación de las "capas de datos / negocios / ui antiguas", es decir, la separación de preocupaciones. Ilustraré esto más tarde, pero por ahora; tenga en cuenta que también se aplica a MVVM
o a cualquier otro patrón de diseño.
¿Es aceptable que un ViewModel contenga instancias de modelos de dominio?
Básicamente no, aunque veo que pasa mucho. Depende un poco del nivel de quick-win
de tu proyecto.
Déjame dar un ejemplo. Imagina el siguiente modelo de vista:
public class FooViewModel
{
public string Name {get; set;}
public DomainClass Genre {get;set;}
}
y la siguiente DomainClass
//also applies to database data/POCO classes
public class DomainClass
{
public int Id {get; set;}
public string Name {get;set;}
}
Por lo tanto, en algún lugar de su controlador, rellena el FooViewModel y lo pasa a su vista.
Ahora, considere los siguientes escenarios:
1) El modelo de dominio cambia.
En este caso, es probable que también deba ajustar la vista, esto es una mala práctica en el contexto de la separación de preocupaciones.
Si ha separado el ViewModel del DomainModel, un ajuste menor en las asignaciones (ViewModel => DomainModel (y viceversa)) sería suficiente.
2) La clase de dominio tiene propiedades anidadas y su vista solo muestra el GenreName
del GenreName
.
He visto que esto va mal en escenarios reales.
En este caso, un problema común es que el uso de @Html.EdittorFor
conducirá a entradas para el objeto anidado. Esto podría incluir Id
s y otra información sensible. Siguiendo este curso, te encontrarás creando entradas hidden
. Si combinas esto con un modelo de enlace del lado del servidor o automapper, es muy difícil bloquear la manipulación de Id
ocultos con herramientas como firebug.
Aunque es posible, quizás fácil, bloquear algunos de esos campos, mientras más objetos de Dominio / Datos anidados tenga, más difícil será asegurar esta parte. Y tenga en cuenta que es posible que desee cambiar su modelo de dominio por una razón que no necesariamente está orientada a la vista. Por lo tanto, con cada cambio en su DomainModel debe tener en cuenta que podría afectar la vista y los aspectos de seguridad del controlador.
3) En asp.net-MVC es común usar atributos de validación.
¿Realmente desea que su dominio contenga metadatos sobre sus vistas? ¿O aplicar la lógica de visualización a su capa de datos? ¿Es su validación de vista siempre lo mismo que la validación de dominio? ¿Tiene la misma lógica de validación? ¿Está utilizando su aplicación cruzada de modelos de dominio? etc.
Creo que está claro que esta no es la ruta a seguir.
4 más
Puedo darte más escenarios, pero es solo una cuestión de gusto por lo que es más atractivo. Solo espero que en este punto obtendrás el punto :) Sin embargo, prometí una ilustración:
Ahora, para quick-wins
realmente sucios y quick-wins
funcionará, pero no creo que debas quererlo.
Es solo un poco más de esfuerzo construir un modelo de vista, que generalmente es similar al 80% del modelo de dominio. Esto puede parecer como hacer mapeos innecesarios, pero cuando surge la primera diferencia conceptual, encontrará que valió la pena el esfuerzo :)
Entonces, como alternativa, propongo la siguiente configuración para un caso general:
- crear un modelo de vista
- crear un modelo de dominio
- crear un modelo de datos
- use una biblioteca como
automapper
para crear una asignación de una a otra (esto ayudará a asignarFoo.FooProp
aOtherFoo.FooProp
)
Los beneficios son, por ejemplo; Si crea un campo adicional en una de las tablas de su base de datos, no afectará su vista. Puede golpear su capa de negocios o asignaciones, pero allí se detendrá. Por supuesto, la mayoría de las veces también desea cambiar su vista, pero en este caso no es necesario . Por lo tanto, mantiene el problema aislado en una parte de su código.
api web / capa de datos
Otro ejemplo concreto de cómo funcionará esto en un escenario Web-API / EF:
Nota
Como dijo @mrjoltcola: también hay un componente de sobreingeniería para tener en cuenta. Si no se aplica lo anterior, y se puede confiar en los usuarios / programadores, está listo. Pero tenga en cuenta que la capacidad de mantenimiento y la reutilización disminuirán debido a la mezcla de DomainModel / ViewModel.
Soy bastante nuevo en el uso de ViewModels y me pregunto: ¿es aceptable que un ViewModel contenga instancias de modelos de dominio como propiedades, o deberían ser las propiedades de esos modelos de dominio propiedades del propio ViewModel? Por ejemplo, si tengo una clase Album.cs
public class Album
{
public int AlbumId { get; set; }
public string Title { get; set; }
public string Price { get; set; }
public virtual Genre Genre { get; set; }
public virtual Artist Artist { get; set; }
}
Por lo general, haría que ViewModel retuviera una instancia de la clase Album.cs
o que ViewModel tuviera propiedades para cada una de las propiedades de la clase Album.cs
.
public class AlbumViewModel
{
public Album Album { get; set; }
public IEnumerable<SelectListItem> Genres { get; set; }
public IEnumerable<SelectListItem> Artists { get; set; }
public int Rating { get; set; }
// other properties specific to the View
}
public class AlbumViewModel
{
public int AlbumId { get; set; }
public string Title { get; set; }
public string Price { get; set; }
public IEnumerable<SelectListItem> Genres { get; set; }
public IEnumerable<SelectListItem> Artists { get; set; }
public int Rating { get; set; }
// other properties specific to the View
}
Las opiniones varían, desde una mezcla de mejores prácticas técnicas y preferencias personales.
No hay nada de malo en usar objetos de dominio en sus modelos de vista, o incluso usar objetos de dominio como su modelo, y muchas personas lo hacen. Algunos creen firmemente en crear modelos de vista para cada vista individual, pero personalmente, creo que muchas aplicaciones están sobre diseñadas por desarrolladores que aprenden y repiten un enfoque con el que se sienten cómodos. La verdad es que hay varias formas de lograr el objetivo utilizando versiones más recientes de ASP.NET MVC.
El mayor riesgo, cuando usa una clase de dominio común para su modelo de vista y su capa de negocio y persistencia, es el de la inyección de modelos. Agregar nuevas propiedades a una clase de modelo puede exponer esas propiedades fuera del límite del servidor. Un atacante puede ver propiedades que no debería ver (serialización) y alterar valores que no debería alterar (modelar carpetas).
Para protegerse contra la inyección, use prácticas seguras que sean relevantes para su enfoque general. Si planea usar objetos de dominio, entonces asegúrese de usar listas blancas o listas negras (inclusión / exclusión) en el controlador o mediante anotaciones de carpetas de modelos. Las listas negras son más convenientes, pero los desarrolladores perezosos que escriben futuras revisiones pueden olvidarse de ellas o no ser conscientes de ellas. Las listas blancas ([Bind (Include = ...)] son obligatorias y requieren atención cuando se agregan nuevos campos, por lo que actúan como un modelo de vista en línea.
Ejemplo:
[Bind(Exclude="CompanyId,TenantId")]
public class CustomerModel
{
public int Id { get; set; }
public int CompanyId { get; set; } // user cannot inject
public int TenantId { get; set; } // ..
public string Name { get; set; }
public string Phone { get; set; }
// ...
}
o
public ActionResult Edit([Bind(Include = "Id,Name,Phone")] CustomerModel customer)
{
// ...
}
La primera muestra es una buena manera de imponer la seguridad de multitenant en toda la aplicación. La segunda muestra permite personalizar cada acción.
Sea consistente en su enfoque y documente claramente el enfoque utilizado en su proyecto para otros desarrolladores.
Le recomiendo que siempre use modelos de vista para las funciones relacionadas con el inicio de sesión / perfil para obligarse a "marshall" los campos entre el controlador web y la capa de acceso a datos como un ejercicio de seguridad.