ASP .NET Core - Usando o EF Core em um projeto separado - II

 Neste artigo vou mostrar como usar e configurar o EF Core em um projeto separado na criação de uma aplicação ASP .NET Core MVC.

Continuando o artigo anterior vamos iniciar as definições dos demais projetos em nossa solução.

Configurando o projeto Application

Todos os nossos serviços, interfaces e ViewModels serão definidos no projeto Receitas.Application.

A seguir vamos criar as seguintes pastas no projeto para organizar o código:

Vamos começar definindo as ViewModels.

Uma ViewModel representa os dados que você deseja exibir em sua view ou página, seja usando texto estático ou  valores de entrada (como caixas de texto e listas suspensas) que podem ser adicionados ao banco de dados.

Portanto a ViewModel é diferente do seu modelo de domínio. É um modelo usado apenas para a View, ou seja, ela cria uma máscara para os modelos de domínio.

Assim vamos criar uma nova classe na pasta ViewModels chamada ReceitaViewModel onde obteremos uma lista das receitas do banco de dados, e a seguir inclua o seguinte código na classe ReceitaViewModel.cs :

using Receitas.Domain.Entities;
using System.Collections.Generic;
namespace Receitas.Application.ViewModels
{
    public class ReceitaViewModel
    {
        public IEnumerable<Receita> Receitas { get; set; }
    }
}

Observe que estamos obtendo os dados do projeto a partir de Receitas.Domain.Entities.

A seguir vamos criar uma interface para atuar como um contrato para a funcionalidade que estamos tentando implementar.

Na pasta Interfaces, crie uma nova interface chamada IReceitaService com o seguinte código:

using Receitas.Application.ViewModels;
namespace Receitas.Application.Interfaces
{
    public interface IReceitaService
    {
        ReceitaViewModel GetReceitas();
    }
}

Quando implementarmos esta interface o método GetReceitas() retornará uma lista de receitas, e ele só conhece a ViewModel, não sabe nada sobre o modelo de domínio central Receita, então estamos abstraindo a entidade central fazendo isso, ao invés de ter tudo em um só lugar.

Antes de escrevermos a implementação para IReceitaService, temos que definir uma maneira de obter os dados do banco de dados. Para fazer isso, podemos usar a ferramenta ORM Entity Framework, mas aqui vamos usar o padrão Repository para separar a ´lógica do negócio e as camadas de acesso a dados em nosso aplicativo. (você não obrigado a usar o padrão Repository)

A definição formal do padrão Repository é a seguinte:

'O padrão Repository medeia entre o domínio e as camadas de mapeamento de dados usando uma interface semelhante a uma coleção para acessar os objetos do domínio.'

Podemos dizer que o padrão Repositório atua como um intermediário ou uma camada intermediária entre o restante do aplicativo e a lógica de acesso aos dados.

Vamos iniciar a implementação criando uma pasta Interfaces no projeto Receitas.Domain e nesta pasta Interfaces vamos criar o arquivo IReceitaRepository.cs e inclua o código a seguir:

using Receitas.Domain.Entities;
using System.Collections.Generic;
namespace Receitas.Domain.Interfaces
{
    public interface IReceitaRepository
    {
        IEnumerable<Receita> GetReceitas();
    }
}

Neste ponto, se o projeto MVC ou a camada de apresentação (que não conhece nada sobre a entidade de domínio Receita) solicitar uma lista de produtos, ela vai precisar pedir isso a um serviço que iremos implementar a seguir e que chamaremos de ReceitaService e que vai usar a interface IReceitaService.

Agora de onde ReceitaService vai obter os dados ?

Este serviço vai obter os dados a partir de ReceitaRepository que é o repositório que iremos implementar a partir de IReceitaRepository.

Então vamos criar primeiro o serviço ReceitaService na pasta Services do projeto Receitas.Application.

using Receitas.Application.Interfaces;
using Receitas.Application.ViewModels;
using Receitas.Domain.Interfaces;
using System;
namespace Receitas.Application.Services
{
    public class ReceitaService : IReceitaService
    {
        public IReceitaRepository _receitaRepository;
        public ReceitaService(IReceitaRepository receitaRepository)
        {
            _receitaRepository = receitaRepository;
        }

        public ReceitaViewModel GetReceitas()
        {
            throw new NotImplementedException();
        }
    }
}

Observe que estamos injetando uma instância do repositório no construtor do serviço.

Assim precisamos implementar agora o repositório ReceitaRepository, e vamos fazer isso no projeto Recietas.Infra

Primeiro vamos criar a pasta Repositories neste projeto  a seguir criar nesta pasta o arquivo ReceitaRepository com o código abaixo:

using Receitas.Domain.Entities;
using Receitas.Domain.Interfaces;
using Receitas.Infra.Context;
using System.Collections.Generic;
namespace Receitas.Infra.Repositories
{
    internal class ReceitaRepository : IReceitaRepository
    {
        private AppDbContext _context;
        public ReceitaRepository(AppDbContext context)
        {
            _context = context;
        }

        public IEnumerable<Receita> GetReceitas()
        {
            return _context.Receitas;
        }
    }
}

Note que aqui estamos injetando uma instância do nosso contexto AppDbContext no construtor.

Agora podemos completar a implementação do serviço ReceitaService na pasta Services do projeto Receitas.Application com o código a seguir:

using Receitas.Application.Interfaces;
using Receitas.Application.ViewModels;
using Receitas.Domain.Interfaces;

namespace Receitas.Application.Services
{
    internal class ReceitaService : IReceitaService
    {
        public IReceitaRepository _receitaRepository;
        public ReceitaService(IReceitaRepository receitaRepository)
        {
            _receitaRepository = receitaRepository;
        }

        public ReceitaViewModel GetReceitas()
        {
            return new ReceitaViewModel()
            {
                Receitas = _receitaRepository.GetReceitas()
            };
        }

    }
}

Agora que já temos os serviços e o repositório criados precisamos configurar essas dependências do nosso projeto Receitas.Infra definindo a conexão entre as nossas interfaces e suas implementações a partir de vários projetos em um único ponto de referência.

Esse é o propósito da classe DependencyInjection e do método de extensão AddInfrastrucure. Vamos então incluir a definição do serviço e do repositório neste método:

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Receitas.Application.Interfaces;
using Receitas.Application.Services;
using Receitas.Domain.Interfaces;
using Receitas.Infra.Context;
using Receitas.Infra.Repositories;

namespace Receitas.Infra
{
    public static class DependencyInjection
    {
        public static IServiceCollection AddInfrastructure(this IServiceCollection services,
            IConfiguration configuration)
        {
            services.AddDbContext<AppDbContext>(options =>
                options.UseSqlServer(
                    configuration.GetConnectionString("DefaultConnection"),
                    b => b.MigrationsAssembly(typeof(AppDbContext)
                            .Assembly.FullName)));

            services.AddScoped<IReceitaService, ReceitaService>();
            services.AddScoped<IReceitaRepository, ReceitaRepository>();

            services.AddDatabaseDeveloperPageExceptionFilter();

            return services;
        }
    }
}

Observe que usamos o tempo de vida AddScoped no registro dos serviços, com isso os serviços são criados uma vez por solicitação do cliente (conexão).

Finalmente agora todas as nossas camadas estão prontas. Só falta a camada de apresentação.

A seguir, vamos dar uma olhada em como criar os controladores, usando tudo o que discutimos aqui e implementar a interface com o usuário no projeto MVC.

Na próxima parte do artigo vamos concluir a nossa implementação criando o controlador e a view para exibir os dados das receitas.

"Bem-aventurado o homem que sofre a tentação; porque, quando for provado, receberá a coroa da vida, a qual o Senhor tem prometido aos que o amam."
Tiago 1:12

Referências:


José Carlos Macoratti