Tenho um processo de registro de várias etapas , apoiado por um único objeto na camada de domínio , que tem regras de validação definidas nas propriedades.
Como devo validar o objeto de domínio quando o domínio é dividido em muitas visualizações e preciso salvar o objeto parcialmente na primeira visualização quando postado?
Pensei em usar Sessões, mas isso não é possível porque o processo é demorado e a quantidade de dados é alta, então não quero usar a sessão.
Pensei em salvar todos os dados em um banco de dados relacional na memória (com o mesmo esquema do banco de dados principal) e, em seguida, liberar esses dados para o banco de dados principal, mas surgiram problemas porque devo rotear entre os serviços (solicitados nas visualizações) que trabalham com o banco de dados principal e banco de dados na memória.
Estou procurando uma solução elegante e limpa (mais precisamente uma melhor prática).
ATUALIZAÇÃO E Esclarecimento:
@Darin Obrigado por sua resposta atenciosa, Isso foi exatamente o que fiz até agora. Mas, aliás, eu tenho uma solicitação que tem muitos anexos, eu desenho um, Step2View
por exemplo, qual usuário pode fazer upload de documentos de forma assíncrona, mas esses anexos devem ser salvos em uma tabela com relação referencial a outra tabela que deveria ter sido salva antes em Step1View
.
Portanto, devo salvar o objeto de domínio em Step1
(parcialmente), mas não posso, porque o objeto de Domínio Núcleo apoiado que é mapeado parcialmente para um ViewModel de Step1 não pode ser salvo sem adereços que vêm de convertidos Step2ViewModel
.
fonte
Respostas:
Primeiro, você não deve usar nenhum objeto de domínio em suas visualizações. Você deve usar modelos de visualização. Cada modelo de visualização conterá apenas as propriedades que são exigidas por determinada visualização, bem como os atributos de validação específicos para esta determinada visualização. Portanto, se você tiver um assistente de 3 etapas, isso significa que você terá 3 modelos de visualização, um para cada etapa:
e assim por diante. Todos esses modelos de visualização podem ser apoiados por um modelo de visualização do assistente principal:
então você poderia ter ações do controlador renderizando cada etapa do processo do assistente e passando o principal
WizardViewModel
para a visualização. Quando você estiver na primeira etapa dentro da ação do controlador, poderá inicializar aStep1
propriedade. Então, dentro da visão, você geraria o formulário permitindo ao usuário preencher as propriedades sobre a etapa 1. Quando o formulário for enviado, a ação do controlador aplicará as regras de validação apenas para a etapa 1:Agora, na visualização da etapa 2, você pode usar o auxiliar Html.Serialize de MVC futuros para serializar a etapa 1 em um campo oculto dentro do formulário (uma espécie de ViewState, se desejar):
e dentro da ação POST da etapa 2:
E assim por diante até chegar à última etapa em que terá o
WizardViewModel
preenchido com todos os dados. Em seguida, você mapeará o modelo de visualização para seu modelo de domínio e o passará para a camada de serviço para processamento. A camada de serviço pode executar qualquer regra de validação e assim por diante ...Existe também outra alternativa: usar javascript e colocar tudo na mesma página. Existem muitos plug - ins jquery por aí que fornecem funcionalidade de assistente ( Stepy é um bom exemplo). É basicamente uma questão de mostrar e ocultar divs no cliente, caso em que você não precisa mais se preocupar com a persistência do estado entre as etapas.
Mas não importa a solução que você escolher, sempre use modelos de visualização e execute a validação nesses modelos de visualização. Enquanto você estiver utilizando atributos de validação de anotação de dados em seus modelos de domínio, terá muita dificuldade, pois os modelos de domínio não são adaptados para visualizações.
ATUALIZAR:
OK, devido aos inúmeros comentários, concluo que a minha resposta não foi clara. E devo concordar. Portanto, deixe-me tentar elaborar mais detalhadamente meu exemplo.
Poderíamos definir uma interface que todos os modelos de visualização de etapas devem implementar (é apenas uma interface de marcador):
então, definiríamos 3 etapas para o assistente, em que cada etapa, é claro, conteria apenas as propriedades necessárias, bem como os atributos de validação relevantes:
a seguir, definimos o modelo de visualização do assistente principal, que consiste em uma lista de etapas e um índice de etapa atual:
Em seguida, passamos para o controlador:
Algumas observações sobre este controlador:
[Deserialize]
atributos da biblioteca Microsoft Futures, portanto, certifique-se de ter instalado oMvcContrib
NuGet. Essa é a razão pela qual os modelos de visualização devem ser decorados com o[Serializable]
atributoIStepViewModel
interface, portanto, para fazer sentido, precisamos de um fichário de modelo personalizado.Aqui está o fichário de modelo associado:
Esse fichário usa um campo oculto especial denominado StepType que conterá o tipo concreto de cada etapa e que enviaremos em cada solicitação.
Este modelo de fichário será registrado em
Application_Start
:A última parte que falta no quebra-cabeça são as visualizações. Esta é a
~/Views/Wizard/Index.cshtml
visão principal :E isso é tudo de que você precisa para fazer isso funcionar. Claro, se você quiser, pode personalizar a aparência de algumas ou todas as etapas do assistente definindo um modelo de editor personalizado. Por exemplo, vamos fazer isso para a etapa 2. Portanto, definimos um
~/Views/Wizard/EditorTemplates/Step2ViewModel.cshtml
parcial:Veja como a estrutura se parece:
Claro que há espaço para melhorias. A ação Index POST se parece com s..t. Há muito código nele. Uma simplificação adicional envolveria mover todas as coisas de infraestrutura como índice, gerenciamento de índice atual, cópia da etapa atual no assistente, ... para outro fichário de modelo. Então, finalmente, terminamos com:
que é mais como as ações POST devem se parecer. Estou deixando essa melhoria para a próxima vez :-)
fonte
Para complementar a resposta de Amit Bagga, você encontrará abaixo o que fiz. Mesmo que menos elegante, acho esse caminho mais simples do que a resposta de Darin.
Controlador:
Modelos:
fonte
Eu sugeriria que você mantenha o estado de Processo Completo no cliente usando Jquery.
Desta forma, você pode construir facilmente seu objeto de domínio diretamente a partir dos dados de postagem do formulário e, caso os dados contenham erros, retorne um JSON válido contendo todas as mensagens de erro e exibi-los em um div.
Divida as etapas
fonte
Assistentes são apenas etapas simples no processamento de um modelo simples. Não há razão para criar vários modelos para um assistente. Tudo que você faria é criar um único modelo e passá-lo entre as ações em um único controlador.
O coed acima é estúpido e simples, então substitua seus campos lá. Em seguida, começamos com uma ação simples que inicia nosso assistente.
Isso chama a visualização de "WizardStep1.cshtml (se estiver usando razor). Você pode usar o assistente de criação de modelo se quiser. Estaremos apenas redirecionando a postagem para uma ação diferente.
O importante é que postaremos isso em uma ação diferente; a ação WizardStep2
Nesta ação, verificamos se nosso modelo é válido e, em caso afirmativo, o enviamos para nossa view WizardStep2.cshtml, caso contrário, o enviamos de volta à etapa um com os erros de validação. Em cada etapa, enviamos para a próxima etapa, validar essa etapa e seguir em frente. Agora, alguns desenvolvedores experientes podem dizer que não podemos nos mover entre etapas como esta se usarmos atributos [Obrigatórios] ou outras anotações de dados entre as etapas. E você estaria certo, então remova os erros nos itens que ainda não foram verificados. como abaixo.
Por fim, salvaríamos o modelo uma vez no armazenamento de dados. Isso também evita que um usuário inicie um assistente, mas não o conclua, de não salvar dados incompletos no banco de dados.
Espero que você ache este método de implementação de um assistente muito mais fácil de usar e manter do que qualquer um dos métodos mencionados anteriormente.
Obrigado pela leitura.
fonte
Eu queria compartilhar minha própria maneira de lidar com esses requisitos. Eu não queria usar SessionState, nem queria lidar com o lado do cliente, e o método serialize requer MVC Futures que eu não queria incluir em meu projeto.
Em vez disso, construí um HTML Helper que irá iterar por todas as propriedades do modelo e gerar um elemento oculto personalizado para cada uma. Se for uma propriedade complexa, ela será executada recursivamente nela.
Em seu formulário, eles serão postados no controlador junto com os novos dados do modelo a cada etapa do "assistente".
Eu escrevi isso para MVC 5.
Agora, para todas as etapas do seu "assistente", você pode usar o mesmo modelo básico e passar as propriedades do modelo "Etapa 1,2,3" para o auxiliar @ Html.HiddenClassFor usando uma expressão lambda.
Você pode até ter um botão Voltar em cada etapa, se desejar. Apenas tenha um botão voltar em seu formulário que irá postá-lo em uma ação StepNBack no controlador usando o atributo formaction. Não incluído no exemplo abaixo, mas apenas uma ideia para você.
De qualquer forma, aqui está um exemplo básico:
Aqui está o seu MODELO
Aqui está o seu CONTROLADOR
Aqui estão suas VIEWS
Passo 1
Passo 2
etapa 3
fonte
Adicionando mais informações da resposta de @Darin.
E se você tiver um estilo de design separado para cada etapa e desejar manter cada uma em uma visualização parcial separada ou se você tiver várias propriedades para cada etapa?
Durante o uso
Html.EditorFor
, temos limitações para usar a visualização parcial.Crie 3 vistas parciais na
Shared
pasta chamada:Step1ViewModel.cshtml , Step3ViewModel.cshtml , Step3ViewModel.cshtml
Para resumir, acabei de postar a primeira visão parcial, as outras etapas são as mesmas da resposta de Darin.
Step1ViewModel.cs
Step1ViewModel.cshtml
Index.cshtml
Se houver alguma solução melhor, por favor, comente para que outros saibam.
fonte
Uma opção é criar um conjunto de tabelas idênticas que armazenarão os dados coletados em cada etapa. Então, na última etapa, se tudo correr bem, você pode criar a entidade real copiando os dados temporários e armazenando-os.
Outra é criar
Value Objects
para cada etapa e armazená-la emCache
ouSession
. Então, se tudo correr bem, você pode criar seu objeto Domínio a partir deles e salvá-lofonte