Tengo un proceso de registro de varios pasos , respaldado por un solo objeto en la capa de dominio , que tiene reglas de validación definidas en las propiedades.
¿Cómo debo validar el objeto de dominio cuando el dominio está dividido en muchas vistas y tengo que guardar el objeto parcialmente en la primera vista cuando se publica?
Pensé en usar Sessions, pero eso no es posible porque el proceso es largo y la cantidad de datos es alta, así que no quiero usar session.
Pensé en guardar todos los datos en una base de datos relacional en memoria (con el mismo esquema que la base de datos principal) y luego vaciar esos datos a la base de datos principal, pero surgieron problemas porque debería enrutar entre los servicios (solicitados en las vistas) que trabajan con el base de datos principal y base de datos en memoria.
Estoy buscando una solución elegante y limpia (más precisamente una buena práctica).
ACTUALIZACIÓN Y aclaración:
@Darin Gracias por su reflexiva respuesta, eso fue exactamente lo que hice hasta ahora. Pero, por cierto, tengo una solicitud que tiene muchos archivos adjuntos, diseño un, Step2View
por ejemplo, qué usuario puede cargar documentos de forma asincrónica, pero esos archivos adjuntos deben guardarse en una tabla con relación referencial a otra tabla que debería haberse guardado antes en Step1View
.
Por lo tanto, debería guardar el objeto de dominio en Step1
(parcialmente), pero no puedo, porque el objeto de dominio principal respaldado que se asigna parcialmente a un modelo de vista de Step1 no se puede guardar sin los accesorios que provienen de la conversión Step2ViewModel
.
fuente
Respuestas:
Primero, no debería utilizar ningún objeto de dominio en sus vistas. Debería utilizar modelos de vista. Cada modelo de vista contendrá solo las propiedades requeridas por la vista dada, así como los atributos de validación específicos de esta vista. Entonces, si tiene un asistente de 3 pasos, esto significa que tendrá 3 modelos de vista, uno para cada paso:
y así. Todos esos modelos de vista podrían estar respaldados por un modelo de vista del asistente principal:
entonces podría tener acciones de controlador que representen cada paso del proceso del asistente y pasen el principal
WizardViewModel
a la vista. Cuando esté en el primer paso dentro de la acción del controlador, puede inicializar laStep1
propiedad. Luego, dentro de la vista, generaría el formulario que permite al usuario completar las propiedades sobre el paso 1. Cuando se envía el formulario, la acción del controlador aplicará las reglas de validación para el paso 1 únicamente:Ahora, dentro de la vista del paso 2, puede usar el ayudante Html.Serialize de futuros MVC para serializar el paso 1 en un campo oculto dentro del formulario (una especie de ViewState si lo desea):
y dentro de la acción POST del paso 2:
Y así sucesivamente hasta que llegues al último paso donde tendrás el
WizardViewModel
relleno con todos los datos. Luego, mapeará el modelo de vista a su modelo de dominio y lo pasará a la capa de servicio para su procesamiento. La capa de servicio podría realizar cualquier regla de validación por sí misma, etc.También hay otra alternativa: usar javascript y poner todo en la misma página. Hay muchos complementos de jquery que brindan funcionalidad de asistente ( Stepy es uno bueno). Básicamente, se trata de mostrar y ocultar divs en el cliente, en cuyo caso ya no tendrá que preocuparse por el estado persistente entre los pasos.
Pero no importa qué solución elija, utilice siempre modelos de vista y realice la validación en esos modelos de vista. Mientras pegue atributos de validación de anotaciones de datos en sus modelos de dominio, tendrá muchas dificultades, ya que los modelos de dominio no se adaptan a las vistas.
ACTUALIZAR:
De acuerdo, debido a los numerosos comentarios, saco la conclusión de que mi respuesta no fue clara. Y debo estar de acuerdo. Permítanme intentar desarrollar más mi ejemplo.
Podríamos definir una interfaz que deberían implementar todos los modelos de vista de pasos (es solo una interfaz de marcador):
luego definiríamos 3 pasos para el asistente donde cada paso, por supuesto, contendría solo las propiedades que requiere, así como los atributos de validación relevantes:
a continuación, definimos el modelo de vista del asistente principal, que consta de una lista de pasos y un índice de pasos actual:
Luego pasamos al controlador:
Un par de comentarios sobre este controlador:
[Deserialize]
atributos de la biblioteca Microsoft Futures, así que asegúrese de haber instaladoMvcContrib
NuGet. Esa es la razón por la que los modelos de vista deben decorarse con el[Serializable]
atributoIStepViewModel
interfaz, por lo que para que esto tenga sentido, necesitamos una carpeta de modelos personalizada.Aquí está la carpeta de modelos asociada:
Esta carpeta utiliza un campo oculto especial llamado StepType que contendrá el tipo concreto de cada paso y que enviaremos en cada solicitud.
Este modelo de carpeta quedará registrado en
Application_Start
:La última parte del rompecabezas que falta son las vistas. Aquí está la
~/Views/Wizard/Index.cshtml
vista principal :Y eso es todo lo que necesita para que esto funcione. Por supuesto, si lo desea, puede personalizar la apariencia de algunos o todos los pasos del asistente definiendo una plantilla de editor personalizada. Por ejemplo, hagámoslo para el paso 2. Entonces definimos un
~/Views/Wizard/EditorTemplates/Step2ViewModel.cshtml
parcial:Así es como se ve la estructura:
Por supuesto, hay margen de mejora. La acción Index POST se parece a s..t. Hay demasiado código en él. Una simplificación adicional implicaría mover todas las cosas de la infraestructura como el índice, la administración del índice actual, la copia del paso actual en el asistente, ... en otra carpeta de modelos. Para que finalmente terminemos con:
que es más cómo deberían verse las acciones POST. Dejo esta mejora para la próxima vez :-)
fuente
Para complementar la respuesta de Amit Bagga, encontrará a continuación lo que hice. Incluso si es menos elegante, encuentro esta forma más simple que la respuesta de Darin.
Controlador :
Modelos:
fuente
Le sugiero que mantenga el estado de Proceso completo en el cliente usando Jquery.
De esta manera, puede construir fácilmente su objeto de dominio directamente desde los datos de publicación del formulario y, en caso de que los datos tengan errores, devuelva un JSON válido que contenga todos los mensajes de error y los muestre en un div.
Divide los pasos
fuente
Los asistentes son simples pasos para procesar un modelo simple. No hay ninguna razón para crear varios modelos para un asistente. Todo lo que haría es crear un solo modelo y pasarlo entre acciones en un solo controlador.
La alumna anterior es estúpidamente simple, así que reemplace sus campos allí. A continuación, comenzamos con una acción simple que inicia nuestro asistente.
Esto llama a la vista "WizardStep1.cshtml (si usa una maquinilla de afeitar). Puede usar el asistente de creación de plantillas si lo desea. Simplemente redirigiremos la publicación a una acción diferente.
Lo importante es que publicaremos esto en una acción diferente; la acción WizardStep2
En esta acción verificamos si nuestro modelo es válido, y si es así lo enviamos a nuestra vista WizardStep2.cshtml, de lo contrario lo enviamos al paso uno con los errores de validación. En cada paso lo enviamos al siguiente paso, validamos ese paso y seguimos adelante. Ahora, algunos desarrolladores inteligentes podrían decir que no podemos movernos entre pasos como este si usamos atributos [Requeridos] u otras anotaciones de datos entre pasos. Y estaría en lo cierto, así que elimine los errores en los elementos que aún no se han verificado. como abajo.
Finalmente, guardaríamos el modelo una vez en el almacén de datos. Esto también evita que un usuario inicie un asistente pero no lo finalice para no guardar datos incompletos en la base de datos.
Espero que este método para implementar un asistente le resulte mucho más fácil de usar y mantener que cualquiera de los métodos mencionados anteriormente.
Gracias por leer.
fuente
Quería compartir mi propia forma de manejar estos requisitos. No quería usar SessionState en absoluto, ni quería que se manejara en el lado del cliente, y el método de serialización requiere MVC Futures que no quería tener que incluir en mi proyecto.
En su lugar, construí un HTML Helper que iterará a través de todas las propiedades del modelo y generará un elemento oculto personalizado para cada una. Si es una propiedad compleja, se ejecutará de forma recursiva en ella.
En su formulario, se publicarán en el controlador junto con los datos del nuevo modelo en cada paso del "asistente".
Escribí esto para MVC 5.
Ahora, para todos los pasos de su "asistente", puede utilizar el mismo modelo base y pasar las propiedades del modelo "Paso 1,2,3" al ayudante @ Html.HiddenClassFor utilizando una expresión lambda.
Incluso puede tener un botón de retroceso en cada paso si lo desea. Solo tenga un botón de retroceso en su formulario que lo publicará en una acción StepNBack en el controlador usando el atributo de formación. No se incluye en el siguiente ejemplo, pero es solo una idea para ti.
De todos modos, aquí hay un ejemplo básico:
Aquí está tu MODELO
Aquí está tu CONTROLADOR
Aquí están tus VISTAS
Paso 1
Paso 2
Paso 3
fuente
Añadiendo más información de la respuesta de @ Darin.
¿Qué pasa si tiene un estilo de diseño separado para cada paso y desea mantener cada uno en una vista parcial separada o qué sucede si tiene varias propiedades para cada paso?
Mientras lo usamos,
Html.EditorFor
tenemos una limitación para usar la vista parcial.Cree 3 vistas parciales en la
Shared
carpeta llamada:Step1ViewModel.cshtml , Step3ViewModel.cshtml , Step3ViewModel.cshtml
Por brevedad, acabo de publicar la primera vista parcial, otros pasos son los mismos que la respuesta de Darin.
Step1ViewModel.cs
Step1ViewModel.cshtml
Index.cshtml
Si hay alguna solución mejor, comente para informar a otros.
fuente
Una opción es crear un conjunto de tablas idénticas que almacenarán los datos recopilados en cada paso. Luego, en el último paso, si todo va bien, puede crear la entidad real copiando los datos temporales y almacenarlos.
Otro es crear
Value Objects
para cada paso y almacenar luego enCache
oSession
. Luego, si todo va bien, puede crear su objeto de dominio a partir de ellos y guardarlofuente