.NET

2 jan, 2019

.NET Core – Implementando o padrão Repositório – Parte 02

Publicidade

Hoje veremos como implementar o padrão repositório na plataforma .NET Core.

Aplicarei o conceito de arquitetura em camadas para realizar um nível mínimo de separação de responsabilidades e de organização de código.

Na abordagem usada, criaremos uma solução no VS 2017 Community contendo três projetos:

  • Projeto da camada de acesso a dados contendo a implementação do repositório
  • Projeto da camada de negócios
  • Projeto console para consumir e testar a implementação

Aplicarei alguns padrões de projeto como o padrão Repository e o padrão Unit Of Work e vou usar o Entity Framework Core para persistência.

Implementaremos um repositório genérico, onde vamos definir a interface IRepository<T> e a sua implementação na classe Repositoy<T>.

Vamos implementar o padrão Unit Of Work, que pode ser visto como um contexto, sessão ou objeto que acompanha as alterações das entidades de negócio durante uma transação sendo, também responsável pelo gerenciamento dos problemas de concorrência, que podem ocorrer oriundos dessa transação.

Da mesma forma, vamos criar uma interface IUnitOfWork e a classe UnitOfWork, que implementa esta interface.

Para materializar esse repositório genérico, vou definir um repositório para um domínio Cliente, onde vou criar a interface IClienteRepository e sua implementação ClienteRepository, e assim gerenciar as informações de Cliente fazendo um CRUD básico, definindo essa lógica na camada de negócios.

Abaixo, vemos o diagrama de classes para os artefatos gerados no projeto:

Assim, o padrão de repositório é a abordagem para abstrair os detalhes da camada de acesso a dados do restante do aplicativo. É muito mais fácil usar um repositório genérico para evitar que a lógica de negócios se misture com a lógica de acesso a dados.

O repositório atua como um mediador entre a camada de acesso a dados e as camadas de negócios do aplicativo. Ele consulta a fonte de dados, mapeia os dados da fonte de dados para uma entidade, e persiste alterações na entidade para a fonte de dados.

Dentre os benefícios em usar o padrão repositório, temos:

  • A centralização da lógica de acesso a dados.
  • Facilidade na realização dos testes unitários.
  • Uma arquitetura flexível que pode ser adaptada à medida em que o design geral do aplicativo evolui.

Recursos:

  • Visual Studio 2017 Community
  • Entity Framework Core 2.1

Criando o projeto no VS 2017 Community

Abra o VS 2017 Community e crie uma solução em branco via menu File > New Project e em seguida selecione o template Other Type Projects > Visual Studio Solution.

Feito isso, clique em Blank Solution e informe o nome EstudoRepo:

A seguir, inclua um novo projeto do tipo Class Library nesta solução via menu File > Add > New Project. Selecione o template Class Library(.NET Standard), informe o nome DataAccess e clique em OK:

Exclua o arquivo Class1.cs e crie neste projeto três pastas:

  • Context: contém o arquivo de contexto da aplicação;
  • Domain: contém o modelo de domínio da aplicação;
  • Repository: contém as interfaces e classes que implementam o nosso repositório;

Agora incluiremos uma referência no projeto ao Entity Framework Core que vamos usar para acesso a dados e persistência.

No menu Tools, siga o caminho Nuget Package Manager > Manage Nuget Packages for Solution, e na guia Browse, selecione: entityframeworkcore.sqlserver e clique no botão Install.

Depois de concluir estes passos, seu projeto deverá possuir a seguinte estrutura:

Definindo o modelo de domínio

O modelo de domínio representa o domínio da nossa aplicação que, para o exemplo deste artigo, será representado por uma classe Cliente, que representa os dados dos clientes que gerenciaremos.

Na pasta Domain inclua a classe Cliente com o código abaixo:

public class Cliente
{
        public int ClienteId { get; set; }
        public string Nome { get; set; }
        public string Email { get; set; }
}

Também usaremos essa classe como um Data transfer object. (DTO).

Definindo a classe de contexto

Como eu vou usar o EF Core, tenho que criar uma classe de contexto que herda de DbContext, onde vou definir o provedor para o banco de dados e a string de conexão com o banco de dados SQL Server que iremos acessar.

Na pasta Context, crie uma classe chamada AppDbContext com o código abaixo:

using DataAccess.Domain;
using Microsoft.EntityFrameworkCore;
namespace DataAccess.Context
{
    public class AppDbContext : DbContext
    {
        public DbSet<Cliente> Clientes { get; set; }

        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        { }
        public AppDbContext()
        { }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Data Source=Macoratti;Initial Catalog=Cadastro;Integrated Security=True");
        }
    }
}

Nesta classe, definimos o seguinte:

  • 1 – O mapeamento da entidade Cliente para a tabela Clientes via DbSet
  • 2 – O provedor do banco de dados SQL Server
  • 3 – A string de conexão, indicando o servidor e banco de dados usados

Acessaremos o banco de dados Cadastro.mdf, que já existe no SQL Server e a tabela Clientes, que possui os seguintes dados:

Implementando o repositório

Começaremos desenvolvendo nosso Repositório definindo uma interface chamada IRepository<T> na pasta Repository.

A interface definida será bem simples – com apenas alguns métodos. Assim não teremos uma implementação completa, mas sim um exemplo que serve para destacar o tipo de funções mais comuns que são implementadas em um padrão de repositório.

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace DataAccess.Repository
{
    public interface IRepository<T> where T : class
    {
        IEnumerable<T> Get();
        IEnumerable<T> Get(Expression<Func<T, bool>> predicate);
        T GetById(Expression<Func<T, bool>> predicate);
        void Add(T entity);
        void Delete(T entity);
        void Update(T entity);
    }
}

Faremos algumas observações sobre o código usado:

  • 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 – IEnumerable<T> Get(): este método retorna os dados como IEnumerable
  • 4 – IEnumerable<T> Get(Expression<Func<T, bool>> predicate): retorna os dados que atendem ao critério informado em tempo de execução via expressão lambda. Estamos usando o delegate Func e aplicando o predicate para verificar se o dado atende o critério (retorna true ou false)
  • 5 – void Add(T entity): recebe o objeto T para realizar a inclusão no banco de dados
  • 6 – void Update(T entity): recebe o objeto T para realizar a atualização no banco de dados
  • 7 – void Delete(T entity): recebe o objeto T e realiza a exclusão no banco de dados

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 definiremos a classe que implementará esse contrato.

Implementando a interface IRepositorio na classe Repositorio

Vamos então criar uma classe chamada Repositorio, que implementará a nossa interface.

Selecione a pasta Repository, e no menu PROJECT clique em Add New Item. Em seguida, selecione o template Class, informe o nome Repositorio.cs e clique no botão Add.

Inclua o código abaixo nesta classe:

using DataAccess.Context;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace DataAccess.Repository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        private readonly AppDbContext _context;
        public Repository(AppDbContext context)
        {
            _context = context;
        }
        public void Add(T entity)
        {
            _context.Set<T>().Add(entity);
        }
        public void Delete(T entity)
        {
            _context.Set<T>().Remove(entity);
        }
        public void Update(T entity)
        {
            _context.Entry(entity).State = EntityState.Modified;
            _context.Set<T>().Update(entity);
        }
        public IEnumerable<T> Get()
        {
            return _context.Set<T>().AsEnumerable<T>();
        }
        public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
        {
            return _context.Set<T>().Where(predicate).AsEnumerable<T>();
        }
        public T GetById(Expression<Func<T, bool>> predicate)
        {
            return _context.Set<T>().SingleOrDefault(predicate);
        }   
    }
}

Na próxima parte do artigo explicaremos a implementação feita na classe Repository<T>.