
Hoje venho apresentar uma forma simples e produtiva para persistência e manipulação de dados.
Para isso vou utilizar a seguinte arquitetura:
Camada de Apresentação : Telas Windows Form
Camada de Entidades: Classes referente aos objetos de negócio do projeto
Camada de Repositório: Classes relacionadas aos objetos de negócios, com a responsabilidade de persistência e manipulação de dados.
Para o acesso aos dados, implementarei os repositórios utilizando o ADO.NET SqlClient. O que por um lado vai gerar um pouco mais de serviço, precisando implementar todos os comandos SQL para manipulação dos dados. Por outro lado, será um ponto positivo. Pois vamos ter a autonomia nessas manipulações, por não estar dependendo de Frameworks ORMs e tendo todas as possibilidades de comandos que o SQL pode nos proporcionar.
O código do projeto foi compartilhado no Github: https://github.com/maxclaus/WindowsForm-Entidades-RepositorioAdoNet
Projeto Entidades🔗
Aqui implementamos dentro do projeto de Entidades, nossa classe referente ao objeto Aluno. Tente sempre manter, para as classes de entidade, a real características e comportamentos referente ao objeto que a classe representa. Não deixe métodos CRUD aqui, porque no mundo real um aluno não possui tais comportamentos.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MDI_Escola.Entidades
{
public class Aluno
{
public int Id { get; set; }
public string Nome { get; set; }
public string Email { get; set; }
public string Telefone { get; set; }
public string Endereco { get; set; }
public int Genero { get; set; }
public bool Ativo { get; set; }
}
}
Projeto Repositório🔗
A classe a seguir será herdada por todos os repositórios concretos. Nela manteremos métodos e atributos comum aos outros repositórios. Neste caso, foi implementado o método criar conexão, que recupera a string de conexão que está no arquivo app.config, que está localizado no projeto principal da Solution (Camada Apresentação).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Configuration;
namespace MDI_Escola.Repositorios
{
public abstract class RepositorioBase
{
protected SqlConnection CriarConexao()
{
return new SqlConnection
{
ConnectionString = ConfigurationManager.
ConnectionStrings["MDI_Escola"].
ConnectionString
};
}
}
}
}
}
Interface que define os comportamentos comuns ao todos os repositórios. Dessa forma garantimos que todos os repositórios deverão seguir esta implementação. O mais legal nesse ponto, é que foi utilizado generics. Deixando esta interface bem genérica e possibilitando que qualquer classe consiga implementá-la.
using System.Collections.Generic;
namespace MDI_Escola.Repositorios
{
public interface IRepositorio<T> where T : class
{
T Obter(int id);
bool Inserir(T entidade);
bool Excluir(int id);
bool Atualizar(T entidade);
List<T> Todos();
}
}
Repositório Aluno implementa a interface IRepositorio e herda as definições da classe RepositorioBase. Aqui sim realizamos o acesso aos dados, através do SqlClient do ADO.NET.
using System;
using System.Collections.Generic;
using System.Linq;
using MDI_Escola.Entidades;
using System.Data.SqlClient;
using System.Data;
namespace MDI_Escola.Repositorios
{
public class AlunoRepositorio : RepositorioBase, IRepositorio<Aluno>
{
public Aluno Obter(int id)
{
var comando = new SqlCommand
{
CommandText = "SELECT * FROM alunos WHERE id = @id"
};
Aluno aluno = null;
try
{
using (comando.Connection = CriarConexao())
{
comando.Connection.Open();
comando.Parameters.Add(new SqlParameter("id", id));
var reader = comando.ExecuteReader();
while (reader.Read())
{
aluno = new Aluno
{
Id = (int)reader["id"],
Nome = (string)reader["nome"],
Email = (string)reader["email"],
Telefone = (string)reader["telefone"],
Endereco = (string)reader["endereco"],
Ativo = (bool)reader["ativo"],
Genero = (int)reader["genero"]
};
}
reader.Dispose();
}
}
catch (Exception ex)
{
throw new ArgumentException("Erro ao retornar aluno do banco. Msg: " + ex.Message);
}
finally
{
comando.Dispose();
}
return aluno;
}
public bool Inserir(Aluno entidade)
{
var comando = new SqlCommand
{
CommandText = "INSERT INTO alunos " +
"VALUES(@nome,@email,@telefone,@endereco,@genero,@ativo)"
};
try
{
using (comando.Connection = CriarConexao())
{
comando.Connection.Open();
comando.Parameters.Add(new SqlParameter("nome", entidade.Nome));
comando.Parameters.Add(new SqlParameter("email", entidade.Email));
comando.Parameters.Add(new SqlParameter("telefone", entidade.Telefone));
comando.Parameters.Add(new SqlParameter("endereco", entidade.Endereco));
comando.Parameters.Add(new SqlParameter("genero", entidade.Genero));
comando.Parameters.Add(new SqlParameter("ativo", entidade.Ativo));
comando.ExecuteNonQuery();
return true;
}
}
catch (Exception ex)
{
throw new ArgumentException("Erro ao inserir. Msg:" + ex.Message);
}
finally
{
comando.Dispose();
}
}
public bool Excluir(int id)
{
var comando = new SqlCommand
{
CommandText = "DELETE FROM alunos WHERE id = @id"
};
try
{
using (comando.Connection = CriarConexao())
{
comando.Connection.Open();
comando.Parameters.Add(new SqlParameter("id", id));
comando.ExecuteNonQuery();
return true;
}
}
catch (Exception ex)
{
throw new ArgumentException("Erro ao excluir aluno. Msg: " + ex.Message);
}
finally
{
comando.Dispose();
}
}
public bool Atualizar(Aluno entidade)
{
var comando = new SqlCommand
{
CommandText = "UPDATE alunos SET " +
"nome = @nome, " +
"email = @email, " +
"telefone = @telefone, " +
"endereco = @endereco, " +
"genero = @genero, " +
"ativo = @ativo, " +
"where id = @id "
};
try
{
using (comando.Connection = CriarConexao())
{
comando.Connection.Open();
comando.Parameters.Add(new SqlParameter("id", entidade.Id));
comando.Parameters.Add(new SqlParameter("nome", entidade.Nome));
comando.Parameters.Add(new SqlParameter("email", entidade.Email));
comando.Parameters.Add(new SqlParameter("telefone", entidade.Telefone));
comando.Parameters.Add(new SqlParameter("endereco", entidade.Endereco));
comando.Parameters.Add(new SqlParameter("genero", entidade.Genero));
comando.Parameters.Add(new SqlParameter("ativo", entidade.Ativo));
comando.ExecuteNonQuery();
return true;
}
}
catch(Exception ex)
{
throw new ArgumentException("Erro ao executar comando no banco. Msg:" + ex.Message);
}
finally
{
comando.Dispose();
}
}
public List<Aluno> Todos()
{
var comando = new SqlCommand
{
CommandText = "SELECT * FROM alunos"
};
var listaAlunos = new List<Aluno>();
using (comando.Connection = CriarConexao())
{
comando.Connection.Open();
var reader = comando.ExecuteReader();
while (reader.Read())
{
var alunodb = new Aluno
{
Id = (int)reader["id"],
Nome = (string)reader["nome"],
Email = (string)reader["email"],
Telefone = (string)reader["telefone"],
Endereco = (string)reader["endereco"],
Genero = (int)reader["genero"],
Ativo = (bool)reader["ativo"]
};
listaAlunos.Add(alunodb);
}
reader.Dispose();
comando.Dispose();
}
return listaAlunos;
}
public bool VerificarNomeDisponivel(string nome, int id)
{
var comando = new SqlCommand
{
CommandText = "PR_VerificarNomeDisponivel",
CommandType = CommandType.StoredProcedure
};
try
{
using (comando.Connection = CriarConexao())
{
comando.Connection.Open();
comando.Parameters.Add(new SqlParameter("NOME", nome));
comando.Parameters.Add(new SqlParameter("ID", id));
var resultado = comando.ExecuteScalar();
if (resultado == null)
return true;
else
return false;
}
}
catch (Exception ex)
{
throw new ArgumentException("Erro ao verificar disponibilidade de nome. Msg:" + ex.Message);
}
finally
{
comando.Dispose();
}
}
}
}
Note que no código anterior utilizamos 3 tipos de "Execute Command", para executar os comandos SQL.
n ExecuteScalar: Executa o comando SQL e retorna a primeira coluna da primeira linha do conjunto de resultados retornado na consulta. As restantes linhas e colunas são ignoradas. Normalmente utilizado para COUNT, SUM e outros desse tipo.
- ExecuteReader: Executa o comando SQL retorna uma variável que fornece uma maneira de ler um fluxo uni-direcional de uma base de dados SQL Server. Utilizado para comandos de SELECT.
- ExecuteNonQuery: Executa o comando SQL e retorna o número de linhas afetadas. Normalmente utilizado para comandos INSERT, DELETE e UPDATE.
Projeto MDI_Escola (Apresentação)🔗
A tela para gerenciamento do Aluno ficará assim:

Eventos da tela Cadastrar Aluno (CodeBehind da Tela):
using System;
using System.Windows.Forms;
using MDI_Escola.Entidades;
using MDI_Escola.Repositorios;
namespace WindowsForms_MDI_Escola
{
public partial class FrmCadAluno : Form
{
#region Propriedades
private AlunoRepositorio _repositorioAluno;
#endregion
#region Metodos
public FrmCadAluno()
{
_repositorioAluno = new AlunoRepositorio();
InitializeComponent();
LimparCampos();
}
protected void LimparCampos()
{
lblCodigo.Text =
txtNome.Text =
txtEmail.Text =
txtEndereco.Text =
txtTelefone.Text = string.Empty;
rbFem.Checked =
rbMasc.Checked =
chkAtivo.Checked =
btnAlterar.Enabled =
btnCancelar.Enabled =
btnExcluir.Enabled = false;
btnSalvar.Enabled = true;
dataGridViewAlunos.AutoGenerateColumns = false;
dataGridViewAlunos.DataSource = _repositorioAluno.Todos();
}
protected Aluno CarregarObjAluno(int id)
{
var aluno = new Aluno
{
Id = id,
Nome = txtNome.Text,
Email = txtEmail.Text,
Telefone = txtTelefone.Text,
Endereco = txtEndereco.Text,
Ativo = chkAtivo.Checked,
Genero = (rbMasc.Checked ? 0 : 1)
};
return aluno;
}
public void AlterarSituacaoBotao(int id, bool situacao)
{
if (id == 0)
btnSalvar.Enabled = situacao;
else
btnAlterar.Enabled = situacao;
}
#endregion
#region Eventos
private void btnSalvar_Click(object sender, System.EventArgs e)
{
var aluno = CarregarObjAluno(0);
MessageBox.Show(_repositorioAluno.Inserir(aluno)
? "Incluido com sucesso!"
: "Ocorreu um erro ao salvar! Tente novamente.");
LimparCampos();
}
private void dataGridViewAlunos_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
var id = int.Parse(dataGridViewAlunos.CurrentRow.Cells["Id"].Value.ToString());
try
{
var aluno = _repositorioAluno.Obter(id);
if (aluno == null)
{
MessageBox.Show("Aluno não encontrado");
return;
}
lblCodigo.Text = aluno.Id.ToString();
txtNome.Text = aluno.Nome;
txtEmail.Text = aluno.Email;
txtEndereco.Text = aluno.Endereco;
txtTelefone.Text = aluno.Telefone;
chkAtivo.Checked = aluno.Ativo;
rbMasc.Checked = (aluno.Genero == 0);
rbFem.Checked = (aluno.Genero == 1);
btnSalvar.Enabled = false;
btnAlterar.Enabled = btnCancelar.Enabled = btnExcluir.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnAlterar_Click(object sender, EventArgs e)
{
var id = int.Parse(lblCodigo.Text);
var aluno = CarregarObjAluno(id);
MessageBox.Show(_repositorioAluno.Atualizar(aluno) ? "Iserido com sucesso!" : "Erro ao atulizar");
LimparCampos();
}
private void btnCancelar_Click(object sender, EventArgs e)
{
LimparCampos();
}
private void btnExcluir_Click(object sender, EventArgs e)
{
var id = int.Parse(lblCodigo.Text);
if (MessageBox.Show("Deseja excluir?",
"Excluir",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
_repositorioAluno.Excluir(id);
}
LimparCampos();
}
private void txtNome_Leave(object sender, EventArgs e)
{
int id;
int.TryParse(lblCodigo.Text, out id);
var disponivel = _repositorioAluno.VerificarNomeDisponivel(txtNome.Text, id);
lblMsg.Text = disponivel ? string.Empty : "Este nome não está disponível. Tente outro, por favor.";
AlterarSituacaoBotao(id, disponivel);
}
#endregion
}
}
Por último adicione um arquivo "App.config" ao projeto da camada de Apresentação e configure nele a conexão com o seu banco:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="MDI_Escola"
connectionString="Data Source=NT-001;Initial Catalog=MDI_Escola;User ID=sa;Password=123"
providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>