Validando as entidades do domínio utilizando Data Annotation

26/04/2010 at 10:54 3 comentários

Estamos no inicio de um novo projeto aqui na empresa. Este projeto tem o objetivo de substituir o sistema atual utilizado nas lojas. Para atacar um problema de tal complexidade decidimos utilizar uma abordagem baseada no DDD (Domain-Drive Design). Para quem não sabe DDD é um conjunto de técnicas e boas práticas que tem como objetivo fazer com que os artefatos desenvolvidos “falem” a linguagem do negócio. E para isso utiliza diversas técnicas para isolar as classes do domínio.

Uma das primeiras dúvidas que tivemos foi onde colocar a validação das entidades. Discutimos várias alternativas, como colocar a validação na camada de aplicação, nos serviços do domínio e, por fim, nas próprias entidades. Sabiámos que a última opção era a mais correta, porém, não tinhamos noção de como fazer esssa implementação integrada ao ASP.NET.

Um dos nossos desenvolvedores, Vitor Avelino, deu a sugestão de utilizarmos Data Annotations e criar um Validator personalizado para fazer a integração com o ASP.NET, já que o recurso de binding é muito fraco.

Segue um resumo de como implementamos essa solução:

1) Criar um controle Validator com suporte a Data Annotation:

namespace Infraestrutura.Validacao
{
[ToolboxData("<{0}:DataAnnotationValidator runat=server></{0}:DataAnnotationValidator>")]
public class DataAnnotationValidator : BaseValidator
{
public DataAnnotationValidator()
{
Display = ValidatorDisplay.Dynamic;
SetFocusOnError = true;
base.Text = "(!)";
base.ForeColor = System.Drawing.Color.FromName("#990000");
}

#region Properties
/// <summary>
/// The type of the source to check
/// </summary>
public string SourceTypeName { get; set; }

/// <summary>
/// The property that is annotated
/// </summary>
public string PropertyName { get; set; }
#endregion

#region Methods
protected override bool EvaluateIsValid()
{
// get the type that we are going to validate
Type source = GetValidatedType();

// get the property to validate
PropertyInfo property = GetValidatedProperty(source);

string displayPropriedade = property.Name;

// get the control validation value
string value = GetControlValidationValue(ControlToValidate);

foreach (var attribute in property.GetCustomAttributes(typeof(ValidationAttribute), true).OfType<ValidationAttribute>())
{
if (!attribute.IsValid(value))
{
// Carrega o conteúdo do atributo display da propriedade
foreach (var atributoDisplay in property.GetCustomAttributes(typeof(DisplayAttribute), true).OfType<DisplayAttribute>())
{
displayPropriedade = atributoDisplay.Name;
}

switch (attribute.TypeId.GetType().GetProperty("Name").GetValue(attribute.TypeId, null).ToString())
{
case "RequiredAttribute":
ErrorMessage = string.Format(HttpContext.GetGlobalResourceObject("Mensagens", "campo_obrigatorio").ToString(), displayPropriedade);
break;
default:
ErrorMessage = attribute.ErrorMessage;
break;
}
return false;
}
}
return true;
}

private Type GetValidatedType()
{
if (string.IsNullOrEmpty(SourceTypeName))
{
throw new InvalidOperationException("SourceTypeName nulo não pode ser validado.");
}

Type validatedType = Type.GetType(SourceTypeName);
if (validatedType == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}", "SourceTypeName inválido.", SourceTypeName));
}

return validatedType;
}

private PropertyInfo GetValidatedProperty(Type source)
{
PropertyInfo property = source.GetProperty(PropertyName,
BindingFlags.Public | BindingFlags.Instance);

if (property == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}", "Propriedade validada não existe.", PropertyName));
}
return property;
}
#endregion
}
}

2) Decore as propriedades das entidades com os atributos de validação.
public class Acessorio
{
...
[DataMember]
[Required]
[StringLength(100)]
public string Descricao { get; set; }
...

3) As mensagens de erro virão do arquivo WebSite/App_GlobalResources/Mensagens.resx

4) Para validações do tipo RegularExpression a mensagem deve ser decorada no próprio atributo, ao invés de usar o arquivo de resources, já que há uma infinidade de validações possíveis.

5) No formulário, arraste um controle DataAnnotationValidator para cada controle a ser validado.

6) Na propriedade PropertyName do validador coloque o nome da propriedade a ser validada (no caso acima seria Descricao).

7) Em SourceTypeName coloque o namespace e o assembly da classe a ser validada. Exemplo: SourceTypeName=“Dominio.Produtos.Entidades.Acessorio, Dominio.Produtos.Entidades”

8) Garanta que o formulário contenha um sumário para exibir a descrição dos erros.

Lista com as validações disponíveis: http://msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx

[]’s
Eduardo Ernandes da Silva
Arquiteto de Sistemas

Colaboração: Vitor Avelino (Desenvolvedor .Net)

Anúncios

Entry filed under: Domain-Driven Design. Tags: , , , , .

Pangea Scrum e XP direto das Trincheiras

3 Comentários Add your own

  • 1. Eduardo  |  02/08/2011 às 16:53

    Eduardo, realmente DataAnnotation é uma ótima alternativa, porém fiquei com uma dúvida, se utilizamos DDD, uma Premissa seria utilizar POCO nas entidades de dominio?

    Responder
    • 2. eernandes  |  05/08/2011 às 10:56

      Sim, é recomendável utilizar uma classe POCO. O objetivo é deixar o domínio desacoplado das outras camadas e testável.

      Responder
  • 3. Eduardo  |  05/08/2011 às 11:10

    Então, no caso o DataAnnotation inserido em uma classe de domínio não fere a idéia do POCO? Outra pergunta se essa situação está correta, é possível utilizarmos DataAnnotation em uma validação no repositório?

    Responder

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Arquivos

Feeds

Calendário

abril 2010
D S T Q Q S S
« mar   maio »
 123
45678910
11121314151617
18192021222324
252627282930  

%d blogueiros gostam disto: