ASP .NET MVC - CRUD com Repositório Genérico
Neste artigo vamos , mais uma vez, apresentar os conceitos relacionados ao padrão repositório e ao padrão Unit Of Work com um CRUD em uma aplicação ASP .NET MVC 5 usando a linguagem C#. |
Neste artigo volto a falar sobre o padrão repositório e o padrão Unit of Work aplicando na prática os conceitos no desenvolvimento de uma aplicação que realiza um CRUD básico nas informações de livros.
Nosso modelo de domínio portanto será a entidade chamada de Livro que iremos detalhar no decorrer do artigo.
A aplicação desenvolvida vai usar os recursos do BootStrap e do JavaScript para melhorar a experiência do usuário.
Para tornar o entendimento mais simples irei trabalhar com uma única entidade Livro.
Vou definir um repositório genérico para todas as entidades e uma classe UnitOfWork que vai criar uma instância do nosso repositório para cada entidade sendo que o repositório será usado para realizar as operações CRUD - Create, Read, Update e Delete.
O padrão Repository separa a lógica de acesso dados e mapeia essa lógica para entidades na lógica de negócio. Ele trabalhar com as entidades de domínio e realiza a lógica de acesso a dados.
No padrão Repository, as entidades de domínio e a lógica de acesso a dados se comunicam usando interfaces, e isso, esconde os detalhes do acesso a dados da camada de negócios.
Martin Fowler afirma: "O padrão Repository faz a mediação entre o domínio e as camadas de mapeamento de dados, agindo como uma coleção de objetos de domínio em memória..... Conceitualmente, um repositório encapsula o conjunto de objetos persistidos em um armazenamento de dados e as operações realizadas sobre eles, fornecendo uma visão mais orientada a objetos da camada de persistência..... e também dá suporte ao objetivo de alcançar uma separação limpa e uma forma de dependência entre o domínio e as camadas de mapeamento de dados." (http://martinfowler.com/eaaCatalog/repository.html) |
Vamos criar uma instância da classe UnitOfWork no controlador da aplicação ASP .NET MVC e então criar uma instância do repositório dependendo da entidade e posteriormente usar os métodos do repositório conforme as operações CRUD.
No diagrama abaixo temos o relacionamento entre o repositório e o contexto do Entity Framework; os controladores MVC interagem com o repositório através da Unit of Work ao invés de acessar diretamente o Entity Framework.
Vamos definir a classe UnitOfWork e criar uma instância dessa classe de forma que a Unit Of Work irá instanciar o nosso DbContext e a seguir cada instância do repositório usa o mesmo DbContext para realizar as operações no banco de dados. Dessa forma a Unit of Work é o padrão que garante que todos os repositórios irão usar o mesmo contexto do banco de dados.
Vamos utilizar o Entity Framework em uma abordagem Code First.
Nossa aplicação esta baseada em uma arquitetura em camadas onde teremos os seguintes projetos:
Livraria.Core - Contém as entidades e nosso domínio
Livraria.Dados - Contém o DataContext, o Mapeamento da entidade, o Repositório e a Unit of Work
Livraria.Web - Contém a interface com usuário representada pela aplicação ASP .NET MVC
Recursos usados :
Visual Studio Community 2015
Criando a solução e os projetos da aplicação
Abra o VS 2015 Community e clique em New Project;
A seguir selecione Other Project Types -> Visual Studio Solutions;
Informe o nome Livraria e clique no botão OK;
No menu File clique em Add -> New Project e selecione o template Class Library;
Informe o nome Livraria.Core e clique em OK;
Repita o processo acima e crie o projeto Livraria.Dados;
Por último clique no menu File e em Add -> New Project;
Selecione Web -> ASP .NET Web Applications e informe o nome Livraria.Web e clique no botão OK;
A seguir selecione o template MVC conforme mostra a figura abaixo:
Será criado uma solução contendo os projetos e a aplicação MVC contendo toda a estrutura de pastas criadas pelo framework ASP .NET MVC :
Vamos incluir um novo projeto do tipo Class Library em nossa solução.
No menu File clique em Add -> Project e selecione o template Class Library e informe o nome Mvc_Repositorio.Dominio;
Vamos incluir uma referência ao Entity Framework neste projeto via Nuget.
No menu TOOLS clique em Nuget Package Manager e a seguir em Manage Nuget Packages for Solution;
Selecione o Entity Framework e clique no botão Install escolhendo para ser instalado somente no projeto Mvc_Repositorio.Dominio.
Apague o arquivo Class1.cs criado por padrão e a seguir crie uma pasta chamada Repositorio no projeto Mvc_Repositorio.Dominio.
Nesta pasta vamos criar a nossa interface do Repositorio.
No menu PROJECT clique em Add New Item;
Selecione o template Interface e informe o nome IRepositorio.cs
Agora vamos definir os métodos na nossa interface que deverão ser implementados para realizar o acesso e persistência dos dados na camada de acesso a dados.
lembre-se que uma interface é um contrato que define como uma classe deve ser implementada, assim vamos definir assinaturas de métodos que deverão implementados por qualquer classe que desejar usar a nossa interface.
Abaixo vemos os métodos definidos na nossa interface IRepositorio:
using System;
namespace Mvc_Repositorio.Dominio.Repositorio |
Vamos entender o código acima:
1- Note que estamos usamos o namespace
using System.Linq.Expressions
que contém classes e enumerações que permitem representar expressões de código
no nível da linguagem como objetos na forma de árvores de expressões;
2- Na assinatura da classe estamos declarando
public
interface IRepositorio<T> where T : class ; aqui T é uma classe;
3-
IQueryable<T>
GetTodos() - Este método retorna todos os dados como IQueryable;
dessa forma podemos retornar a lista e aplicar expressões lambdas para filtrar e
classificar os dados;
4-
IQueryable<T>
Get(Expression<Func<T, bool>> predicate) - Retorna os dados que
atendem o critério informado em tempo de execução via expressão lambada. Estamos
usando o delegate Func, e aplicando o predicate
para verificar se o dado atende o critério (retorna true ou false);
5-
T Find(params object[] key) - Recebe um array de objetos e efetua
a pesquisa pela chave primária;
6-
T First(Expression<Func<T, bool>> predicate) - Retorna o primeiro
dado que atende o critério informado via expressão lambda. Usamos novamente o
delegate Func e aplicamos o
predicate para verificar se o dado atende o critério;
7-
void Adicionar(T entity) - Recebe o objeto T
para realizar a inclusão no banco de dados;
8-
void Atualizar(T entity) - Recebe o objeto T para realizar a
atualização no banco de dados;
9-
void Deletar(<Func<T, bool>> predicate) - Excluir registros usando
uma condição definida na expressão lambda (via delegate
Func) e aplicando o predicate
(retorna true ou false) para verificar o critério;
10 - void Commit() - Chama o método
ChaveChanges() do contexto para efetivar todas as alterações realizadas no
contexto. Ao final de cada operação você deve sempre chamar este método para
efetivar as operações que foram feitas na memória no banco de dados. Se não
fizer isso irá perder todas as operações realizadas;
11 -
void Dispose() - Executa a
limpeza dos objetos;
Observe que não temos nenhum comando SQL, nenhuma declaração de objetos ADO .NET como connection, command, dataset, datareader, etc.
Já temos o contrato definido e agora vamos definir a classe que irá implementar esse contrato.
Antes de implementar a interface IRepositorio vamos definir o nosso domínio.
Definindo o Domínio
Vamos criar uma pasta chamada Entidades no projeto Mvc_Repositorio.Dominio.
Selecione o projeto e no menu PROJECT clique em New Folder e informe o nome Entidades. Nesta pasta vamos definir as entidades do nosso domínio.
Para tornar as coisas bem simples eu vou definir apenas uma entidade chamada Usuario e vou mapear esta entidade para uma tabela Usuarios existente em um banco SQL Server chamado Cadastro.mdf.
Abaixo vemos a estrutura da tabela Usuarios:
Selecione a pasta criada e no menu PROJECT clique em Add Class e informe o nome Usuario.
Defina o código baixo nesta classe:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc_Repositorio.Dominio.Entidades
{
[Table("Usuarios")]
public class Usuario
{
[Key]
public int UsuarioId { get; set; }
[Required(ErrorMessage = "Informe o login do usuário.")]
[Display(Name = "Usuário")]
public string Nome { get; set; }
[Required(ErrorMessage = "Informe a senha do usuário.")]
[DataType(DataType.Password)]
public string Senha { get; set; }
[Required(ErrorMessage = "Informe o email do usuário.")]
public string Email { get; set; }
}
}
|
O código acima usa os atributos do Data Annotations para definir o mapeamento para a tabela Usuarios e restrições de validações que deverão ser aplicadas na renderização das views que iremos criar.
Agora para realizar o mapeamento ORM vamos definir uma classe onde iremos usar os recursos do Entity Framework através do DbSet.
Selecione a pasta criada e no menu PROJECT clique em Add Class e informe o nome Usuario.
Defina o código baixo nesta classe:
using System.Data.Entity;
namespace Mvc_Repositorio.Dominio.Entidades
{
public class UsuarioContexto : DbContext
{
public UsuarioContexto()
: base("name=ConexaoUsuarios")
{ }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<UsuarioContexto>(new CreateDatabaseIfNotExists<UsuarioContexto>());
}
public DbSet<Usuario> Usuarios { get; set; }
}
}
|
Nesta classe usamos o DbContext e realizamos o mapeamento da entidade Usuario com a tabela Usuarios.
O último detalhe que não podemos esquecer e definir no arquivo web.config da aplicação web a string de conexão ConexaoUsuarios do banco de dados Cadastro.mdf.
Implementando a interface IRepositorio na classe Repositorio
Selecione a pasta Repositorio do projeto de Dominio e selecione o menu PROJECT clique em Add New Item;
Selecione o template Class, informe o nome Repositorio.cs e clique no botão Add;
Vamos definir a assinatura da classe Repositorio conforme a figura abaixo:
using Mvc_Repositorio.Dominio.Entidades;
using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace Mvc_Repositorio.Dominio.Repositorio { public class Repositorio<T> : IRepositorio<T> , IDisposable where T : class { private UsuarioContexto Context; protected Repositorio()
{
Context = new UsuarioContexto();
}
public IQueryable<T> GetTodos()
{
return Context.Set<T>();
}
public IQueryable<T> Get(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().Where(predicate);
}
public T Procurar(params object[] key)
{
return Context.Set<T>().Find(key);
}
public T Primeiro(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().Where(predicate).FirstOrDefault();
}
public void Adicionar(T entity)
{
Context.Set<T>().Add(entity);
}
public void Atualizar(T entity) { Context.Entry(entity).State = EntityState.Modified; } public void Deletar(Func<T, bool> predicate) { Context.Set<T>() .Where(predicate).ToList() .ForEach(del => Context.Set<T>().Remove(del)); } public void Commit() { Context.SaveChanges(); } public void Dispose() { if (Context != null) { Context.Dispose(); } GC.SuppressFinalize(this); } } } |
Dessa forma como já temos o nosso repositório criado vamos definir agora as interfaces para representam o repositório específico para a entidade Usuario.
Então selecione a pasta Repositorio e no menu PROJECT clique em Add New Item;
Selecione o template Interface, informe o nome IUsuarioRepositorio.cs e clique no botão Add;
A seguir defina o código a seguir para a interface IUsuarioRepositorio :
using Mvc_Repositorio.Dominio.Entidades; namespace Mvc_Repositorio.Dominio.Repositorio { public interface IUsuarioRepositorio : IRepositorio<Usuario> { } } |
Deveremos criar também a classe que implementa a interface IUsuarioRepositorio.
Então selecione a pasta Repositorio e no menu PROJECT clique em Add New Item;
Selecione o template Class, informe o nome UsuarioRepositorio.cs e clique no botão Add;
A seguir defina o código a seguir para a classe UsuarioRepositorio :
using Mvc_Repositorio.Dominio.Entidades; namespace Mvc_Repositorio.Dominio.Repositorio { public class UsuarioRepositorio : Repositorio<Usuario>, IUsuarioRepositorio { } } |
Observe que não precisamos definir nenhum código nas interfaces e classes acima pois estamos usando o mecanismo da herança e da implementação da interface e assim estamos usando os métodos definidos na interface IRepositorio e na classe Repositorio. Note também que estamos usando as entidades que foram separadas no projeto de Dominio.
Neste momento nossa solução tem a seguinte estrutura:
Vou parar por aqui, agora cabe a você criar a camada de negócios da aplicação como um exercício.
Para ajudar vou deixar os links de dois artigos onde eu trato do assunto:
.NET - Padrão Repository e Unit of Work com EF 6 (revisitado)
.NET - Padrão Repository e Unit of Work com EF 6 (revisitado) - II
Porque a lei foi dada por Moisés; a
graça e a verdade vieram por Jesus Cristo.
Deus nunca foi visto por alguém. O Filho unigênito, que está no seio do Pai,
esse o revelou.
João 1:17,18
Referências: