ASP 
.NET Core - Criando um Repositório Assíncrono Genérico
    ![]()  | 
    Neste artigo veremos como criar um repositório assíncrono em uma aplicação ASP .NET Core de forma a recordar os conceitos da programação assíncrona. . | 
A programação assíncrona é uma técnica de programação paralela, que permite que o processo de trabalho seja executado separadamente do thread principal do aplicativo. Assim que o trabalho for concluído, ele informa ao tópico principal sobre o resultado, se foi bem-sucedido ou não.
Ao usar a programação assíncrona, podemos evitar gargalos de desempenho e melhorar a capacidade de resposta do nosso aplicativo.
Na programação 
assíncrona não enviamos as requisições para 
o servidor e a bloqueamos enquanto aguardamos as respostas (o tempo que for 
necessário). Agora, quando enviamos uma solicitação ao servidor, o pool de 
threads delega uma thread a essa solicitação.
Eventualmente, essa thread termina seu trabalho e retorna ao pool de threads, 
liberando-se para a próxima solicitação. Em algum momento, os dados serão 
obtidos do banco de dados e o resultado precisa ser enviado ao solicitante. 
Nesse momento, o pool de threads fornece outra thread para lidar com esse 
trabalho, e, uma vez que o trabalho for concluído, a thread  usada volta 
para o pool de threads.
Abaixo temos uma figura representando o fluxo de trabalho assíncrono :

As palavras async, await e os 
tipos de retorno
As palavras-chave async e await desempenham um papel crucial na programação assíncrona. Ao usar essas palavras-chave, podemos escrever facilmente métodos assíncronos sem muito esforço.
Use o modificador async para especificar que um método, uma expressão ou um método anônimo é assíncrono. Se você usar esse modificador em um método ou expressão, ele será referido como um método assíncrono. Exemplo:
| 
		 
		public 
		async Task<int> MetodoAsync()  | 
	
Todo o método onde for usado a palavra async deve conter a palavra await. A palavra-chave await oferece uma maneira sem bloqueio de iniciar uma tarefa e, em seguida, continuar a execução quando essa tarefa for concluída. Exemplo:
Se o método que a palavra-chave async modifica não contiver uma expressão ou instrução await, ele será executado de forma síncrona. Um aviso do compilador o alertará sobre quaisquer métodos assíncronos que não contenham instruções await, pois essa situação poderá indicar um erro.
Mas o que mais 
caracteriza os métodos assíncronos? 
Devemos considerar o seguinte :
A assinatura do método deve incluir o modificador async;
O método deve ter um tipo de retorno da Task<TResult>, Task ou void;
As declarações de método devem incluir pelo menos uma única expressão await - isso diz ao compilador que o método precisa ser suspenso enquanto a operação aguardada estiver ocupada.
Por último, o nome do método deve terminar com o sufixo "async" (mesmo que isso seja mais convencional do que o necessário).
Como exemplo suponha que desejamos criar um método assíncrono para retornar todos os produtos. Para isso temos que usar a palavra-chave async e definir o tipo de retorno do método.
| 
		 async Task<IEnumerable<Produto>> GetTodosProdutosAsync()  | 
	
Ao usar a palavra-chave async, estamos habilitando o uso da palavra-chave await e modificando a forma como os resultados do método são tratados (de síncrono para assíncrono).
A seguir veremos como criar um repositório assíncrono genérico em um projeto ASP .NET Core Web API.
Recursos usados:
Criando o projeto ASP.NET Core Web API na pasta API
Abra o VS 2022, clique em New Project e selecione o template ASP .NET Core Web API e clique em Next;0

Informe o nome ProdutosApi e clique em Next;
A seguir selecione o Framework, Authentication Type e demais configurações conforme mostrada na figura:

Obs: Note que vamos usar Controllers e não vamos usar as minimal APIs.
Clique em Create.
Com o projeto criado vamos criar as pastas Models e Repositoreis no projeto e incluir os seguintes pacotes:
Após isso teremos a seguinte estrutura no projeto :

Na pasta Models vamos criar as classes Produto e Categoria que representam o nosso modelo de domínio:
1- Categoria
| 
		public class Categoria { public int CategriaId { get; set; } public string? Nome { get; set; } public string? Descricao { get; set; } public ICollection<Produto> Produtos { get; set; } }  | 
	
2- Produto
| 
		public class Produto { public int ID { get; set; } public string? Nome { get; set; } public string? Descricao { get; set; } public decimal Preco { get; set; } public string? ImagemUrl { get; set; } public DateTime DataCompra { get; set; } public int Estoque { get; set; } public int CategoriaId { get; set; } public Categoria Categoria { get; set; } }  | 
	
Defini as propriedades de navegação nas entidades para que o EF Core possa inferir o relacionamento entre Categoria e Produto.
A seguir vamos criar nesta pasta a classe de contexto AppDbContext que herda de DbContext:
| 
		using Microsoft.EntityFrameworkCore; namespace ProdutosApi.Models; public class AppDbContext : DbContext  | 
	
No arquivo appsettings.json vamos definir a string de conexão:
| 
		{ "ConnectionStrings": { "DefaultConnection": "Data Source=<sua_instancia>;Initial Catalog=CadastroDB;Integrated Security=True" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }  | 
	
No arquivo Program vamos registrar o serviço do contexto
| 
		var conexaoDB 
		= builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddDbContext<AppDbContext>(options => { options.UseSqlServer(conexaoDB); });  | 
	
Criando os Repositórios e as implementações
Agora na pasta Repositories vamos criar a interface IRepositoryBase() :
| 
		using System.Linq.Expressions; namespace ProdutosApi.Repositories; public interface IRepositoryBasec<T> where T : class  | 
	
Neste código 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;
Note que as assinaturas dos métodos FindAll e FindByCondition retornam IQueryable que nos permite anexar chamadas assíncronas a eles e também podemos aplicar expressões lambdas para filtrar e classificar os dados;
O método
FindByCondidtion() 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
expression para verificar se o dado atende o 
critério (retorna true ou false);
Os métodos Create, Update e Delete não modificam 
nenhum dado, eles apenas rastreiam as alterações em uma entidade e aguardam a 
execução do método SaveChanges do EF Core. 
Observe que na definição da interface do repositório temos uma restrição que diz que o tipo T deve ser uma classe. Existem vários tipos de restrições que podem ser utilizadas para limitar os tipos permitidos. Abaixo temos algumas delas:
Where T: struct, indica que o argumento deve ser um tipo de valor.
Where T: class, indica que T deve ser um tipo de referência.
Where T: new(), obriga que o tipo T tenha um construtor público sem parâmetros;
Where T: nomeClasse, indica que o argumento deve herdar ou ser desse tipo.
Where T: nomeInterface, o argumento deve implementar a interface indicada.
Where T1: T2, T1 indica que o argumento T1 deve ser igual ou herdar o tipo, também argumento do método, T2.
A seguir vamos criar as interfaces para os repositórios de Categoria e Produto.
1- ICategoriaRepository
| 
		using ProdutosApi.Models; namespace ProdutosApi.Repositories; public interface ICategoriaRepository : IRepositoryBase<Categoria>  | 
	
2- IProdutoRepository
| 
		using ProdutosApi.Models; namespace ProdutosApi.Repositories; public interface IProdutoRepository : IRepositoryBase<Produto>  | 
	
A seguir vamos criar uma interface para agrupar esses dois repositórios:
3- IRepositoryWrapper
| 
		namespace ProdutosApi.Repositories; 
		public interface IRepositoryWrapper  | 
	
A seguir vamos fazer as implementações destas interfaces. Para isso vamos criar uma pasta Implementations dentro da pasta Repositories.
A seguir dentro da pasta Implementations vamos criar cada implementação :
1- BaseRepository
| 
		using Microsoft.EntityFrameworkCore; using ProdutosApi.Models; using System.Linq.Expressions; namespace ProdutosApi.Repositories.Implementations; public abstract class BaseRepository<T> : IRepositoryBase<T> where T : class         public IQueryable<T> FindAll()         public void Create(T entity)         public void Update(T entity)         public void Delete(T entity)  | 
	
2- CategoriaRepository
| 
		using Microsoft.EntityFrameworkCore; using ProdutosApi.Models; namespace ProdutosApi.Repositories.Implementations; public class CategoriaRepository : BaseRepository<Categoria>, ICategoriaRepository         public async Task<IEnumerable<Categoria>> GetAllCategoriasAsync()  | 
	
3- ProdutoRepository
| 
		using Microsoft.EntityFrameworkCore; using ProdutosApi.Models; namespace ProdutosApi.Repositories.Implementations; public class ProdutoRepository : BaseRepository<Produto>, IProdutoRepository         public async Task<IEnumerable<Produto>> GetAllProdutosAsync() 
  | 
	
4- RepositoryWrapper
| 
		using ProdutosApi.Models; 
		namespace ProdutosApi.Repositories.Implementations                 public IProdutoRepository ProdutoRepo                 public RepositoryWrapper(AppDbContext repositoryContext)                 public async Task SaveAsync()  | 
	
Agora podemos implementar os Controllers. Eu vou mostrar a implementação do controlador ProdutosController e vou deixar a seu cargo implementar o controlador para Categorias.
1- ProdutosController
| 
		using Microsoft.AspNetCore.Mvc; using ProdutosApi.Models; using ProdutosApi.Repositories; 
		namespace ProdutosApi.Controllers                 public ProdutosController(ILogger<ProdutosController> logger,
		                 [HttpGet]                 [HttpGet("{id}", Name = "ProdutoById")]                         if (produto == null)                 [HttpPost]                                 if (!ModelState.IsValid)                                 _repoContext.ProdutoRepo.CreateProduto(produto);                                 return CreatedAtRoute("ProdutoById", new { id = produto.ID }, produto);                 [HttpPut("{id}")]                                 if (!ModelState.IsValid)                                 var produtoEntity = await _repoContext.ProdutoRepo.GetProdutoByIdAsync(id);                                 _repoContext.ProdutoRepo.UpdateProduto(produtoEntity);                                 return NoContent();                 [HttpDelete("{id}")]                                 _repoContext.ProdutoRepo.DeleteProduto(produto);                                 return NoContent();  | 
	
Para que tudo funcione corretamente temos que registrar os serviços no arquivo Program:
| 
		using Microsoft.EntityFrameworkCore; using ProdutosApi.Models; using ProdutosApi.Repositories; using ProdutosApi.Repositories.Implementations; 
		var builder = WebApplication.CreateBuilder(args); var conexaoDB = builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddScoped<IRepositoryWrapper, RepositoryWrapper>(); var app = builder.Build(); if (app.Environment.IsDevelopment()) app.UseHttpsRedirection();  | 
	
Executando o projeto teremos o resultado a seguir:

Pegue o projeto 
completo aqui : 
 ProdutosApi_RepoAsync.zip
E estamos 
conversados...
	"Portanto, lembrai-vos de que vós noutro tempo éreis 
	gentios na carne, e chamados incircuncisão pelos que na carne se chamam 
	circuncisão feita pela mão dos homens;
	Que naquele tempo estáveis sem Cristo, separados da comunidade de Israel, e 
	estranhos às alianças da promessa, não tendo esperança, e sem Deus no 
	mundo."
	Efésios 2:11,12
Referências:
ASP .NET Core - Implementando a segurança com
ASP.NET Core MVC - Criando um Dashboard .
C# - Gerando QRCode - Macoratti
ASP .NET - Gerando QRCode com a API do Google
ASP .NET Core 2.1 - Como customizar o Identity
Usando o ASP .NET Core Identity - Macoratti
ASP .NET Core - Apresentando o IdentityServer4
ASP .NET Core 3.1 - Usando Identity de cabo a rabo