Pergunta

Quando comecei a usar xVal para validação do lado do cliente, eu estava implementando apenas métodos de ação que usavam objetos de modelo de domínio como um modelo de visualização ou instâncias incorporadas desses objetos no modelo de visualização.

Essa abordagem funciona bem na maioria das vezes, mas há casos em que a visualização precisa exibir e postar de volta apenas um subconjunto das propriedades do modelo (por exemplo, quando o usuário deseja atualizar sua senha, mas não o restante dos dados de seu perfil). .

Uma solução alternativa (feia) é ter um campo de entrada oculto no formulário para cada propriedade que não esteja presente no formulário.

Aparentemente, a melhor prática aqui é criar um viewmodel personalizado que contenha apenas propriedades relevantes para a view e preencher o viewmodel via Mapeador automático.É muito mais limpo, pois estou transferindo apenas os dados relevantes para a visualização, mas está longe de ser perfeito, pois preciso repetir os mesmos atributos de validação que já estão presentes no objeto do modelo de domínio.

Idealmente, eu gostaria de especificar o objeto Modelo de Domínio como uma metaclasse por meio de um atributo MetaData (isso também é frequentemente chamado de "classe amiga"), mas isso não funciona, pois xVal é lançado quando a classe de metadados possui propriedades que são não está presente no viewmodel.

Existe alguma solução elegante para isso?Estou pensando em hackear o código-fonte do xVal, mas talvez haja alguma outra maneira que esqueci até agora.

Obrigado,

Adriano

Editar: Com a chegada da ASP.NET MVC 2, este não é mais apenas um problema relacionado aos atributos de validação, mas também se aplica aos atributos de editor e exibição.

Foi útil?

Solução

Esta é a razão por excelência pela qual suas telas de entrada não devem ser bem acopladas ao seu modelo. Essa pergunta aparece aqui na etiqueta MVC cerca de 3-4 vezes por mês. Eu enganaria se pudesse encontrar a pergunta anterior e parte da discussão de comentários aqui é interessante. ;)

O problema que você tem é que você está tentando forçar dois contextos de validação diferentes de um modelo em um único modelo que falha em uma grande quantidade de cenários. O melhor exemplo é inscrever um novo usuário e, em seguida, ter um administrador editar um campo de usuário posteriormente. Você precisa validar uma senha em um objeto de usuário durante o registro, mas não mostrará o campo de senha para o administrador editando os detalhes do usuário.

As opções para contornar essas são todas abaixo do ideal. Eu trabalhei nesse problema para três projetos agora e a implementação das seguintes soluções nunca foi limpa e geralmente frustrante. Eu vou tentar ser prático E esqueça todas as discussões DDD/DB/Modelo/Hotness ofthemonth que todos os outros estão tendo.

1) Modelos de visualização múltiplaTer viewmodels quase o mesmo viola o diretor seco, mas sinto que os custos dessa abordagem são realmente baixos. Geralmente, violar os custos de manutenção a seco, mas os custos para isso são os mais baixos e não representam muito. Hipoteticamente falando, você não altera como os personagens do número máximo de nome do último nome pode ter com muita frequência.

2) metadados dinâmicosExistem ganchos no MVC 2 para fornecer seus próprios metadados para um modelo. Com essa abordagem, você pode ter o que estiver usando para fornecer metadados, exclua certos campos com base na atual httprequest e, portanto, ação e controlador. Eu usei essa técnica para criar um sistema de permissões acionado por banco de dados, que vai para o banco de dados e informa à subclasse da DataNoTationsMetAdataProvider para excluir valores baseados em propriedades armazenadas no banco de dados.

Esta técnica está funcionando muito bem, mas o único problema é validar com UpdateModel(). Para resolver esse problema, criamos um SmartUpdateModel() O método que também vai para o banco de dados e gera automaticamente a matriz String exclude [] para que quaisquer campos não permissíveis não sejam validados. É claro que nós armazenamos isso por razões de desempenho, então não é ruim.

Só quero reiterar que usamos [validationattributes] em nossos modelos e os superou com novas regras no tempo de execução. O resultado final foi que o [Required] O campo User.LastName não foi validado se o usuário não tivesse permissão para acessá -lo.

3) coisa de proxy dinâmica da interface loucaA última técnica que tentei foi usar interfaces para o ViewModels. O resultado final foi que eu tinha um objeto de usuário herdado de interfaces como IAdminEdit e IUserRegistration. O iAdminedit e o IUSERRegistration conteriam atributos DataNotation que executaram toda a validação específica do contexto, como uma propriedade de senha com as interfaces.

Isso exigia algum hackery e foi mais um exercício acadêmico do que qualquer outra coisa. O problema com 2 e 3 é que o UpdateModel e o DataNoTationsattribute provedor precisavam ser personalizados para serem informados dessa técnica.

Meu maior obstáculo foi que eu nunca queria enviar todo o objeto de usuário para a visualização, então acabei usando proxies dinâmicos para criar instâncias de tempo de execução de IAdminEdit

Agora eu entendo que essa é uma pergunta muito específica do XVAL, mas todas as estradas para validação dinâmica como essa levam à personalização dos fornecedores internos de metadados do MVC. Como todo o material dos metadados é novo, nada é tão limpo ou simples de fazer neste momento. O trabalho que você deve fazer para personalizar o comportamento de validação do MVC não é difícil, mas exige um conhecimento profundo de como todos os internos funcionam.

Outras dicas

Mudamos nossos atributos de validação para a camada ViewModel. No nosso caso, isso forneceu uma separação mais limpa de preocupações de qualquer maneira, pois conseguimos projetar nosso modelo de domínio para que ele não pudesse entrar em um estado inválido em primeiro lugar. Por exemplo, a data pode ser necessária em um objeto BillingTransaction. Portanto, não queremos torná -lo anulável. Mas em nosso viewmodel, talvez seja necessário expor anulável para que possamos capturar a situação em que o usuário não inseriu um valor.

Em outros casos, você pode ter validação específica por página/formulário, e deseja validar com base no comando que o usuário está tentando executar, em vez de definir várias coisas e perguntar ao modelo de domínio: "Você está Válido para tentar fazer XYZ ", onde, ao fazer" ABC ", esses valores são válidos.

Se os ViewModels estiverem hipoteticamente sendo impostos a você, recomendo que eles apliquem apenas requisitos independentes de domínio.Isso inclui coisas como "o nome de usuário é obrigatório" e "o e-mail está formatado corretamente".

Se você duplicar a validação dos modelos de domínio nos modelos de visualização, terá acoplado fortemente o domínio à UI.Quando a validação do domínio for alterada (“só pode aplicar 2 cupons por semana” se torna “só pode aplicar 1 cupom por semana”), a IU deverá ser atualizada.De modo geral, isso seria horrível e prejudicial à agilidade.

Se você mover a validação dos modelos de domínio para a UI, você basicamente destruiu seu domínio e colocou a responsabilidade da validação na UI.Uma segunda UI teria que duplicar toda a validação, e você acoplou duas UIs separadas.Agora se o cliente deseja uma interface especial para administrar o estoque do seu iPhone, o projeto do iPhone precisa replicar toda a validação que também se encontra na UI do site.Isto seria ainda mais terrível do que a duplicação de validação descrita acima.

A menos que você possa prever o futuro e descartar essas possibilidades, valide apenas os requisitos independentes de domínio.

Não sei como isso será reproduzido pela validação do lado do cliente, mas se a validação parcial for o seu problema, você pode modificar o DataAnnotationsValidationRunner discutido aqui para apreciar um IEnumerable<string> Lista de nomes de propriedades, como segue:

public static class DataAnnotationsValidationRunner
{
     public static IEnumerable<ErrorInfo> GetErrors(object instance, IEnumerable<string> fieldsToValidate)
     {
           return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>().Where(p => fieldsToValidate.Contains(p.Name))
                  from attribute in prop.Attributes.OfType<ValidationAttribute>()
                  where !attribute.IsValid(prop.GetValue(instance))
                  select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
     }
}

Vou arriscar os votos negativos e afirmar que não há benefício para ViewModels (no ASP.NET MVC), especialmente considerando a sobrecarga de criá-los e mantê-los.Se a ideia é desvincular-se do domínio, isso é indefensável.Uma UI dissociada de um domínio não é uma UI para esse domínio.A IU deve dependem do domínio, então você terá suas visualizações/ações acopladas ao modelo de domínio ou sua lógica de gerenciamento de ViewModel acoplada ao modelo de domínio.O argumento da arquitetura é, portanto, discutível.

Se a ideia é evitar que os usuários invadam POSTs HTTP maliciosos que aproveitam a ligação do modelo do ASP.NET MVC para campos mutantes que eles não deveriam ter permissão para alterar, então A) o domínio deve impor esse requisito e B) as ações devem forneça listas de permissões de propriedades atualizáveis ​​para o model binder.

A menos que seu domínio esteja expondo algo maluco, como um gráfico de objeto ativo na memória, em vez de cópias de entidade, os ViewModels são um esforço desperdiçado.Então, para responder sua pergunta, mantenha a validação de domínio no modelo de domínio.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top