Validando Dados com EntityFramework e WindowsForms

Pessoal,

Hoje vou mostrar uma maneira bem simples e interessante de fazer validação de dados com EntityFramework, classes POCO e uma aplicação Windows Forms.

Como eu já comentei em outros posts, quando você utiliza DataAnnotations em classes POCO na sua camada de dados e faz a camada de apresentação com WPF/Silverlight ou MVC, o tratamento dos campos é feito automaticamente, praticamente sem nenhum tipo de código. Mas e se você ainda programa para WindowsForms ou precisa validar os dados da classe em uma camada e retornar o erro para outra camada, como fazer ? Isto é o que veremos a seguir.

Primeiro vamos criar um projeto Windows Forms no Visual Studio 2010:
image

Agora vamos adicionoar o EntityFramework CodeFirst usando o NuGet, como eu expliquei neste post. E logo após vamos criar uma classes chamada Produto e vamos adicionar os annotations nela. Annotations são marcações acima dos campos que definem várias informações ao engine do Entitu Framework, como por exemplo, os valores permitidos no campo.

Nossa classe produto ficará da seguinte maneira:

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: using System.ComponentModel.DataAnnotations;
  6: 
  7: namespace WindowsFormsValidacao
  8: {
  9:     [Table("Produto")]
 10:     public class Produto : ValidacaoEntidade 
 11:     {
 12:         public int ID { get; set; }
 13:         [Required(ErrorMessage="Descrição em branco"])
 14:         public string Descricao { get; set; }
 15:         [Range(1,10000,ErrorMessage="Valor deve estar entre 1 e 10000")]
 16:         public double SaldoMinimo { get; set; }
 17:         [Required(ErrorMessage="Custo em branco")]
 18:         public decimal Custo { get; set; }
 19:         public decimal Venda { get; set; }
 20:         [MaxLength(20,ErrorMessage="Tamanho máximo = 20")]
 21:         public string Localizacao { get; set; }
 22:     }
 23: }

Veja que a nossa classe está herdando de ValidacaoEntidade, que é onde iremos fazer o método de validaçao dos campos, veja abaixo:

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: using System.ComponentModel;
  6: using System.ComponentModel.DataAnnotations;
  7: using System.Xml.Serialization;
  8: 
  9: namespace WindowsFormsValidacao
 10: {
 11:     public class ValidacaoEntidade : IDataErrorInfo
 12:     {
 13:         [NotMapped]
 14:         public string Error
 15:         {
 16:             get { return ""; }
 17:         }
 18: 
 19:         [NotMapped]
 20:         public string this[string columnName]
 21:         {
 22:             get
 23:             {
 24:                 return ValidateProperty(columnName);
 25:             }
 26:         }
 27: 
 28:         protected string ValidateProperty(string propertyName)
 29:         {
 30:             var info = this.GetType().GetProperty(propertyName);
 31: 
 32:             if (!info.CanWrite)
 33:             {
 34:                 return null;
 35:             }
 36: 
 37:             var value = info.GetValue(this, null);
 38:             IEnumerable<string> errorInfos =
 39:                   (from va in info.GetCustomAttributes(true).OfType<ValidationAttribute>()
 40:                    where !va.IsValid(value)
 41:                    select va.FormatErrorMessage(string.Empty)).ToList();
 42: 
 43: 
 44:             if (errorInfos.Count() > 0)
 45:             {
 46:                 return errorInfos.FirstOrDefault<string>();
 47:             }
 48:             return null;
 49:         }
 50: 
 51:         public IEnumerable<string> Validate(bool Formatar=false)
 52:         {
 53:             foreach (var prop in this.GetType().GetProperties())
 54:             {
 55:                 string err = ValidateProperty(prop.Name);
 56:                 if (!String.IsNullOrWhiteSpace(err))
 57:                 {
 58:                     yield return prop.Name + ": " + err + ((Formatar) ? "<br>" : "").ToString();
 59:                 }
 60:             }
 61:         }
 62:     }
 63: }

Nesta classe está o segredo da validação, o método Validate(). Este método percorre todos os campos da nossa classe e verifica seu CustomAttribute(), procurando pelo ValidationAttribute, que é o nosso DataAnnotations, se o valor não for for válido, ele retorna a mensagem de erro que atribuímos ao campos e vai adicionando a uma lista, que poderemos usar em nossa camada de apresentação.

Adicionamos agora vamos adicionar a nossa classe de contexto:

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: using System.Data.Entity;
  6: 
  7: namespace WindowsFormsValidacao
  8: {
  9:     public class Contexto : DbContext
 10:     {
 11:         public DbSet<Produto> Produto { get; set; }
 12:     }
 13: }

Agora vamos adicionar os campos em nosso formulário, que ficará desta forma:

image

O TextBox (txtErros) abaixo do botão Salvar é onde iremos mostrar os erros.

Vamos adicionar um DataSource para podermos trabalhar com nossa classe Produto, para isto vá no menu Data/Add New DataSource e escolha Object:

image

Depois escolha a classe produto:

image

Agora você precisa ligar todos os campos TextBox do formulário aos campos da classe. Para fazer isto, clique sobre o TextBox, vá em propriedades/DataBindings/Text e escolha o campo correspondente ao controle. Faça isto para todos os controles:

image

Após o primeiro campo, escolha os campos sempre a partir do bindingsource:

image

Agora vamos ao código do botão incluir, que simplesmente adiciona um novo objeto Produto vinculado a tela:

  1: private void btnNovo_Click(object sender, EventArgs e)
  2:         {
  3:             produtoBindingSource.AddNew();
  4:         }

E por fim vamos ao código que valida os dados e depois insere no BD:

  1:  private void btnSalvar_Click(object sender, EventArgs e)
  2:         {
  3:             var db = new Contexto();
  4:             db.Database.CreateIfNotExists();
  5: 
  6:             var pro = (Produto)produtoBindingSource.Current;
  7:             var erros = pro.Validate();
  8:             if (erros.Count() > 0)
  9:             {
 10:                 txtErros.Text = "";
 11:                 foreach (var err in erros)
 12:                 {
 13:                     txtErros.Text += err + Environment.NewLine;
 14:                 }
 15:             }
 16:             else
 17:             {
 18:                 db.Produto.Add(pro);
 19:                 db.SaveChanges();
 20:                 MessageBox.Show("Produto inserido!");
 21:                 produtoBindingSource.ResetCurrentItem();
 22:             }
 23:         }

Observação: A criação do banco de dados neste ponto é meramente didática, para facilitar o exemplo e não é uma prática recomendável em uma aplicação em produção.

Agora vamos recuperar o produto que está vinculado a tela e logo após vamos chamar o método Validate() que irá retornar a lista de erros que vamos exibir. . Recuperamos o Produto acessando a propriedade Current do BindingSource, mas como ela armazena somente objetos, precisamos converter o valor de Current para o tipo Produto e logo após isto chamamos a validação e caso não exista erros, inserimos o Produto no BD e atualizamos a tela. Os erros são mostrados no campo txtErros na tela.

Você pode formatar a saída do erro modificando o método Validate() na classe ValidacaoEntidade.

Espero que este pequeno exemplo tenha mostrado o quanto é simples fazer validações mesmo em plataformas não tão novas, como o Windows Forms, e isto pode também ser utilizado para validar objetos em camadas separadas da aplicação.

Você pode baixar o código fonte deste exemplo aqui.

Abraços e até a próxima.

Carlos dos Santos.

5 Comments


  1. Olá, na linha ” db.Database.CreateIfNotExists(); ” do form.cs está retornando o seguinte erro: CREATE DATABASE permission denied in database ‘master’.
    Como posso resolver?
    Grato.

    Reply

  2. Olá,
    Provavelmente o usuário que você está utilizando para se conectar ao banco não tem permissão. Você precisa configurar isto no SQL.
    []s,

    Reply

  3. Muito Bom o post!!! Será que você tem mais destes? Quase não acho material de Winforms com EF…

    Parabéns pelo post!

    Reply

  4. Olá Guilherme,
    Que bom que gostou do post. Sobre Windows Forms não tenho mais nada, mas usar o EF no Windows Forms não é tão diferente do uso com Ado.Net, faça os BindingSource vinculados nas classes que funciona muito bem.
    []s,
    Carlos.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Anti-spam: complete the taskWordPress CAPTCHA


This blog is kept spam free by WP-SpamFree.