From 14649a85ce9c01ed994934a404b99b96a169acab Mon Sep 17 00:00:00 2001 From: Bruno Date: Thu, 15 Feb 2024 17:33:45 -0300 Subject: [PATCH] feature: cached --- OpenAdm.Api/.env | 3 +- .../Controllers/CategoriaController.cs | 30 +++++++ OpenAdm.Api/Controllers/ProdutoController.cs | 44 +++++++++ OpenAdm.Api/Program.cs | 2 +- .../Interfaces/ICategoriaService.cs | 8 ++ .../Interfaces/IProdutoService.cs | 10 +++ .../Services/CategoriaService.cs | 22 +++++ .../Services/ProdutoService.cs | 34 +++++++ .../Interfaces/IProdutoRepository.cs | 3 + OpenAdm.Infra/Cached/Cached/BannerCached.cs | 62 +++++++++++++ .../Cached/Cached/CategoriaCached.cs | 34 +++++++ OpenAdm.Infra/Cached/Cached/ProdutoCached.cs | 85 ++++++++++++++++++ .../Cached/Interfaces/ICachedService.cs | 10 +++ .../Cached/Services/CachedService.cs | 80 +++++++++++++++++ .../Repositories/ProdutoRepository.cs | 89 +++++++++++++++++++ OpenAdm.IoC/DependencyInjectRepositories.cs | 23 ++++- OpenAdm.IoC/DependencyInjectyApplication.cs | 2 + OpenAdm.IoC/OpenAdm.IoC.csproj | 1 + 18 files changed, 536 insertions(+), 6 deletions(-) create mode 100644 OpenAdm.Api/Controllers/CategoriaController.cs create mode 100644 OpenAdm.Api/Controllers/ProdutoController.cs create mode 100644 OpenAdm.Application/Interfaces/ICategoriaService.cs create mode 100644 OpenAdm.Application/Interfaces/IProdutoService.cs create mode 100644 OpenAdm.Application/Services/CategoriaService.cs create mode 100644 OpenAdm.Application/Services/ProdutoService.cs create mode 100644 OpenAdm.Infra/Cached/Cached/BannerCached.cs create mode 100644 OpenAdm.Infra/Cached/Cached/CategoriaCached.cs create mode 100644 OpenAdm.Infra/Cached/Cached/ProdutoCached.cs create mode 100644 OpenAdm.Infra/Cached/Interfaces/ICachedService.cs create mode 100644 OpenAdm.Infra/Cached/Services/CachedService.cs diff --git a/OpenAdm.Api/.env b/OpenAdm.Api/.env index 6f13244..ca43ed0 100644 --- a/OpenAdm.Api/.env +++ b/OpenAdm.Api/.env @@ -6,4 +6,5 @@ AMBIENTE=develop JWT_KEY=1a31ede4-1f1c-4d32-b4a6-b48788d0ff4c JWT_ISSUE=https://api.open-adm.tech JWT_AUDIENCE=OPENADMAUDIENCE -JWT_EXPIRATION=24 \ No newline at end of file +JWT_EXPIRATION=24 +REDIS_URL=201.182.97.170:23647,password=SjK44193CxNH \ No newline at end of file diff --git a/OpenAdm.Api/Controllers/CategoriaController.cs b/OpenAdm.Api/Controllers/CategoriaController.cs new file mode 100644 index 0000000..526bc37 --- /dev/null +++ b/OpenAdm.Api/Controllers/CategoriaController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using OpenAdm.Application.Interfaces; + +namespace OpenAdm.Api.Controllers; + +[ApiController] +[Route("categorias")] +public class CategoriaController : ControllerBaseApi +{ + private readonly ICategoriaService _categoriaService; + + public CategoriaController(ICategoriaService categoriaService) + { + _categoriaService = categoriaService; + } + + [HttpGet("list")] + public async Task GetCategorias() + { + try + { + var categoriasViewModel = await _categoriaService.GetCategoriasAsync(); + return Ok(categoriasViewModel); + } + catch (Exception ex) + { + return await HandleErrorAsync(ex); + } + } +} diff --git a/OpenAdm.Api/Controllers/ProdutoController.cs b/OpenAdm.Api/Controllers/ProdutoController.cs new file mode 100644 index 0000000..1d0c29d --- /dev/null +++ b/OpenAdm.Api/Controllers/ProdutoController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using OpenAdm.Application.Interfaces; + +namespace OpenAdm.Api.Controllers; + +[ApiController] +[Route("produtos")] +public class ProdutoController : ControllerBaseApi +{ + private readonly IProdutoService _produtoService; + + public ProdutoController(IProdutoService produtoService) + { + _produtoService = produtoService; + } + + [HttpGet("list")] + public async Task ListProdutos([FromQuery] int page) + { + try + { + var result = await _produtoService.GetProdutosAsync(page); + return Ok(result); + } + catch (Exception ex) + { + return await HandleErrorAsync(ex); + } + } + + [HttpGet("list-by-categorias")] + public async Task ListProdutosByCategorias([FromQuery] Guid categoriaId) + { + try + { + var result = await _produtoService.GetProdutosByCategoriaIdAsync(categoriaId); + return Ok(result); + } + catch (Exception ex) + { + return await HandleErrorAsync(ex); + } + } +} diff --git a/OpenAdm.Api/Program.cs b/OpenAdm.Api/Program.cs index a9f21cc..f7b6615 100644 --- a/OpenAdm.Api/Program.cs +++ b/OpenAdm.Api/Program.cs @@ -50,7 +50,7 @@ builder.Services.InjectJwt(key, issue, audience); builder.Services.InjectContext(VariaveisDeAmbiente.GetVariavel("STRING_CONNECTION")); -builder.Services.InjectRepositories(); +builder.Services.InjectRepositories(VariaveisDeAmbiente.GetVariavel("REDIS_URL")); builder.Services.InjectServices(); builder.Services.InjectCors(); builder.Services.InjectHttpClient(VariaveisDeAmbiente.GetVariavel("URL_DISCORD")); diff --git a/OpenAdm.Application/Interfaces/ICategoriaService.cs b/OpenAdm.Application/Interfaces/ICategoriaService.cs new file mode 100644 index 0000000..c7875fb --- /dev/null +++ b/OpenAdm.Application/Interfaces/ICategoriaService.cs @@ -0,0 +1,8 @@ +using OpenAdm.Application.Models.Categorias; + +namespace OpenAdm.Application.Interfaces; + +public interface ICategoriaService +{ + Task> GetCategoriasAsync(); +} diff --git a/OpenAdm.Application/Interfaces/IProdutoService.cs b/OpenAdm.Application/Interfaces/IProdutoService.cs new file mode 100644 index 0000000..d5f6dce --- /dev/null +++ b/OpenAdm.Application/Interfaces/IProdutoService.cs @@ -0,0 +1,10 @@ +using OpenAdm.Application.Models.Produtos; +using OpenAdm.Domain.Model; + +namespace OpenAdm.Application.Interfaces; + +public interface IProdutoService +{ + Task> GetProdutosAsync(int page); + Task> GetProdutosByCategoriaIdAsync(Guid categoriaId); +} diff --git a/OpenAdm.Application/Services/CategoriaService.cs b/OpenAdm.Application/Services/CategoriaService.cs new file mode 100644 index 0000000..1a2f591 --- /dev/null +++ b/OpenAdm.Application/Services/CategoriaService.cs @@ -0,0 +1,22 @@ +using OpenAdm.Application.Interfaces; +using OpenAdm.Application.Models.Categorias; +using OpenAdm.Domain.Interfaces; + +namespace OpenAdm.Application.Services; + +public class CategoriaService : ICategoriaService +{ + private readonly ICategoriaRepository _categoriaRepository; + + public CategoriaService(ICategoriaRepository categoriaRepository) + { + _categoriaRepository = categoriaRepository; + } + + public async Task> GetCategoriasAsync() + { + var categorias = await _categoriaRepository.GetCategoriasAsync(); + + return categorias.Select(x => new CategoriaViewModel().ToModel(x)).ToList(); + } +} diff --git a/OpenAdm.Application/Services/ProdutoService.cs b/OpenAdm.Application/Services/ProdutoService.cs new file mode 100644 index 0000000..2230f53 --- /dev/null +++ b/OpenAdm.Application/Services/ProdutoService.cs @@ -0,0 +1,34 @@ +using OpenAdm.Application.Interfaces; +using OpenAdm.Application.Models.Produtos; +using OpenAdm.Domain.Interfaces; +using OpenAdm.Domain.Model; + +namespace OpenAdm.Application.Services; + +public class ProdutoService : IProdutoService +{ + private readonly IProdutoRepository _produtoRepository; + + public ProdutoService(IProdutoRepository produtoRepository) + { + _produtoRepository = produtoRepository; + } + + public async Task> GetProdutosAsync(int page) + { + var paginacao = await _produtoRepository.GetProdutosAsync(page); + + return new PaginacaoViewModel() + { + TotalPage = paginacao.TotalPage, + Values = paginacao.Values.Select(x => new ProdutoViewModel().ToModel(x)).ToList() + }; + } + + public async Task> GetProdutosByCategoriaIdAsync(Guid categoriaId) + { + var produtos = await _produtoRepository.GetProdutosByCategoriaIdAsync(categoriaId); + + return produtos.Select(x => new ProdutoViewModel().ToModel(x)).ToList(); + } +} diff --git a/OpenAdm.Domain/Interfaces/IProdutoRepository.cs b/OpenAdm.Domain/Interfaces/IProdutoRepository.cs index 7d8d5c0..330ea9d 100644 --- a/OpenAdm.Domain/Interfaces/IProdutoRepository.cs +++ b/OpenAdm.Domain/Interfaces/IProdutoRepository.cs @@ -1,8 +1,11 @@ using OpenAdm.Domain.Entities; +using OpenAdm.Domain.Model; namespace OpenAdm.Domain.Interfaces; public interface IProdutoRepository : IGenericRepository { Task> GetProdutosMaisVendidosAsync(); + Task> GetProdutosByCategoriaIdAsync(Guid categoriaId); + Task> GetProdutosAsync(int page); } diff --git a/OpenAdm.Infra/Cached/Cached/BannerCached.cs b/OpenAdm.Infra/Cached/Cached/BannerCached.cs new file mode 100644 index 0000000..a9a3c49 --- /dev/null +++ b/OpenAdm.Infra/Cached/Cached/BannerCached.cs @@ -0,0 +1,62 @@ +using OpenAdm.Domain.Entities; +using OpenAdm.Domain.Interfaces; +using OpenAdm.Domain.Model; +using OpenAdm.Domain.Model.PaginateDto; +using OpenAdm.Infra.Cached.Interfaces; +using OpenAdm.Infra.Context; +using OpenAdm.Infra.Repositories; + +namespace OpenAdm.Infra.Cached.Cached; + +public class BannerCached : GenericRepository, IBannerRepository +{ + private readonly BannerRepository _bannerRepository; + private readonly ICachedService _cachedService; + public BannerCached( + ParceiroContext parceiroContext, + BannerRepository bannerRepository, + ICachedService cachedService) + : base(parceiroContext) + { + _bannerRepository = bannerRepository; + _cachedService = cachedService; + } + + public async Task GetBannerByIdAsync(Guid id) + { + var banner = await _cachedService.GetItemAsync(id.ToString()); + + if (banner == null) + { + banner = await _bannerRepository.GetBannerByIdAsync(id); + if (banner != null) + { + await _cachedService.SetItemAsync(id.ToString(), banner); + } + } + + return banner; + } + + public async Task> GetBannersAsync() + { + var banners = await _cachedService.GetListItemAsync("banners"); + + if (banners == null) + { + banners = await _bannerRepository.GetBannersAsync(); + + if (banners.Count > 0) + { + await _cachedService.SetListItemAsync("banners", banners); + } + } + + return banners; + } + + public async Task> GetPaginacaoBannerAsync(PaginacaoBannerDto paginacaoBannerDto) + { + return await _bannerRepository.GetPaginacaoBannerAsync(paginacaoBannerDto); + } +} diff --git a/OpenAdm.Infra/Cached/Cached/CategoriaCached.cs b/OpenAdm.Infra/Cached/Cached/CategoriaCached.cs new file mode 100644 index 0000000..9814eb9 --- /dev/null +++ b/OpenAdm.Infra/Cached/Cached/CategoriaCached.cs @@ -0,0 +1,34 @@ +using OpenAdm.Domain.Entities; +using OpenAdm.Domain.Interfaces; +using OpenAdm.Infra.Cached.Interfaces; +using OpenAdm.Infra.Context; +using OpenAdm.Infra.Repositories; + +namespace OpenAdm.Infra.Cached.Cached; + +public class CategoriaCached( + ParceiroContext parceiroContext, + CategoriaRepository categoriaRepository, + ICachedService cachedService) : GenericRepository(parceiroContext), ICategoriaRepository +{ + private readonly CategoriaRepository _categoriaRepository = categoriaRepository; + private readonly ICachedService _cachedService = cachedService; + private const string _keyList = "categorias"; + + public async Task> GetCategoriasAsync() + { + var categorias = await _cachedService.GetListItemAsync(_keyList); + + if(categorias == null) + { + categorias = await _categoriaRepository.GetCategoriasAsync(); + + if(categorias.Count > 0) + { + await _cachedService.SetListItemAsync(_keyList, categorias); + } + } + + return categorias; + } +} diff --git a/OpenAdm.Infra/Cached/Cached/ProdutoCached.cs b/OpenAdm.Infra/Cached/Cached/ProdutoCached.cs new file mode 100644 index 0000000..5de8cc7 --- /dev/null +++ b/OpenAdm.Infra/Cached/Cached/ProdutoCached.cs @@ -0,0 +1,85 @@ +using OpenAdm.Domain.Entities; +using OpenAdm.Domain.Interfaces; +using OpenAdm.Domain.Model; +using OpenAdm.Infra.Cached.Interfaces; +using OpenAdm.Infra.Context; +using OpenAdm.Infra.Repositories; + +namespace OpenAdm.Infra.Cached.Cached; + +public class ProdutoCached : GenericRepository, IProdutoRepository +{ + private readonly ProdutoRepository _produtoRepository; + private readonly ICachedService _cachedService; + private const string _keyListMaisVendidos = "produtos-mais-vendidos"; + public ProdutoCached(ParceiroContext parceiroContext, ICachedService cachedService, ProdutoRepository produtoRepository) : base(parceiroContext) + { + _cachedService = cachedService; + _produtoRepository = produtoRepository; + } + + public async Task> GetProdutosMaisVendidosAsync() + { + var produtosMaisVendidos = await _cachedService.GetListItemAsync(_keyListMaisVendidos); + + if (produtosMaisVendidos == null) + { + produtosMaisVendidos = await _produtoRepository.GetProdutosMaisVendidosAsync(); + if (produtosMaisVendidos.Count > 0) + { + await _cachedService.SetListItemAsync(_keyListMaisVendidos, produtosMaisVendidos); + } + } + + return produtosMaisVendidos; + } + + public async Task> GetProdutosAsync(int page) + { + var key = $"produtos-{page}"; + + var produtos = await _cachedService.GetListItemAsync(key); + var count = 0; + + if (produtos == null) + { + var paginacao = await _produtoRepository.GetProdutosAsync(page); + if (paginacao.Values?.Count > 0) + { + produtos = paginacao.Values; + count = paginacao.TotalPage; + await _cachedService.SetListItemAsync(key, paginacao.Values); + } + + } + else + { + count = await _produtoRepository.GetTotalPageProdutosAsync(); + } + + + return new PaginacaoViewModel() + { + TotalPage = count, + Values = produtos + }; + } + + public async Task> GetProdutosByCategoriaIdAsync(Guid categoriaId) + { + var key = $"produtos-por-categoria-{categoriaId}"; + var produtos = await _cachedService.GetListItemAsync(key); + + if (produtos == null) + { + produtos = await _produtoRepository.GetProdutosByCategoriaIdAsync(categoriaId); + + if (produtos != null) + { + await _cachedService.SetListItemAsync(key, produtos); + } + } + + return produtos ?? new List(); + } +} diff --git a/OpenAdm.Infra/Cached/Interfaces/ICachedService.cs b/OpenAdm.Infra/Cached/Interfaces/ICachedService.cs new file mode 100644 index 0000000..b594285 --- /dev/null +++ b/OpenAdm.Infra/Cached/Interfaces/ICachedService.cs @@ -0,0 +1,10 @@ +namespace OpenAdm.Infra.Cached.Interfaces; + +public interface ICachedService where T : class +{ + Task GetItemAsync(string key); + Task?> GetListItemAsync(string key); + Task SetListItemAsync(string key, IList itens); + Task SetItemAsync(string key, T item); + Task RemoveCachedAsync(string key); +} diff --git a/OpenAdm.Infra/Cached/Services/CachedService.cs b/OpenAdm.Infra/Cached/Services/CachedService.cs new file mode 100644 index 0000000..405359c --- /dev/null +++ b/OpenAdm.Infra/Cached/Services/CachedService.cs @@ -0,0 +1,80 @@ +using Microsoft.Extensions.Caching.Distributed; +using OpenAdm.Domain.Exceptions; +using OpenAdm.Infra.Cached.Interfaces; +using OpenAdm.Infra.Factories.Interfaces; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OpenAdm.Infra.Cached.Services; + +public class CachedService : ICachedService where T : class +{ + private readonly IDistributedCache _distributedCache; + private readonly DistributedCacheEntryOptions _options; + private readonly JsonSerializerOptions _serializerOptions; + private static readonly double _absolutExpiration = 60; + private static readonly double _slidingExpiration = 30; + private readonly IDomainFactory _domainFactory; + + public CachedService(IDistributedCache distributedCache, + IDomainFactory domainFactory) + { + _serializerOptions = new() + { + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + WriteIndented = true + }; + _options = new DistributedCacheEntryOptions() + .SetAbsoluteExpiration(TimeSpan.FromMinutes(_absolutExpiration)) + .SetSlidingExpiration(TimeSpan.FromMinutes(_slidingExpiration)); + + _distributedCache = distributedCache; + _domainFactory = domainFactory; + } + + public async Task GetItemAsync(string key) + { + Valid(key); + var value = await _distributedCache.GetStringAsync(GetNewKey(key)); + return value is null ? null : JsonSerializer.Deserialize(value, _serializerOptions); + } + + public async Task?> GetListItemAsync(string key) + { + Valid(key); + var values = await _distributedCache.GetStringAsync(GetNewKey(key)); + return values is null ? null : JsonSerializer.Deserialize>(values, _serializerOptions); + } + + public async Task RemoveCachedAsync(string key) + { + Valid(key); + await _distributedCache.RemoveAsync(GetNewKey(key)); + } + + public async Task SetItemAsync(string key, T item) + { + Valid(key); + var valueJson = JsonSerializer.Serialize(item, options: _serializerOptions); + await _distributedCache.SetStringAsync(GetNewKey(key), valueJson, _options); + } + + public async Task SetListItemAsync(string key, IList itens) + { + Valid(key); + var valuesJson = JsonSerializer.Serialize>(itens, options: _serializerOptions); + await _distributedCache.SetStringAsync(GetNewKey(key), valuesJson, _options); + } + + private static void Valid(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ExceptionApi("Key do cached inválida!"); + } + + private string GetNewKey(string key) + { + return $"{_domainFactory.GetDomainParceiro()}-{key}"; + } +} diff --git a/OpenAdm.Infra/Repositories/ProdutoRepository.cs b/OpenAdm.Infra/Repositories/ProdutoRepository.cs index 8564069..9bbc60d 100644 --- a/OpenAdm.Infra/Repositories/ProdutoRepository.cs +++ b/OpenAdm.Infra/Repositories/ProdutoRepository.cs @@ -1,7 +1,9 @@ using Microsoft.EntityFrameworkCore; using OpenAdm.Domain.Entities; using OpenAdm.Domain.Interfaces; +using OpenAdm.Domain.Model; using OpenAdm.Infra.Context; +using OpenAdm.Infra.Extensions.IQueryable; namespace OpenAdm.Infra.Repositories; @@ -9,6 +11,51 @@ public class ProdutoRepository(ParceiroContext parceiroContext) : GenericRepository(parceiroContext), IProdutoRepository { private readonly ParceiroContext _parceiroContext = parceiroContext; + private const int _take = 5; + + public async Task> GetProdutosAsync(int page) + { + var newPage = page == 0 ? page : page - 1; + + var totalPages = await _parceiroContext + .Produtos + .AsQueryable() + .TotalPage(_take); + + var produtos = await _parceiroContext + .Produtos + .AsNoTracking() + .AsQueryable() + .OrderBy(x => x.Numero) + .Include(x => x.Categoria) + .Skip(newPage * _take) + .Take(_take) + .ToListAsync(); + + var tamanhos = await _parceiroContext + .TamanhosProdutos + .AsNoTracking() + .Include(x => x.Tamanho) + .ToListAsync(); + + if (produtos.Count > 0) + { + produtos.ForEach(produto => + { + produto.Categoria.Produtos = new(); + produto.Tamanhos = tamanhos + .Where(x => x.ProdutoId == produto.Id) + .Select(tm => new Tamanho(tm.Tamanho.Id, tm.Tamanho.DataDeCriacao, tm.Tamanho.DataDeAtualizacao, tm.Tamanho.Numero, tm.Tamanho.Descricao)) + .ToList(); + }); + } + + return new PaginacaoViewModel() + { + TotalPage = totalPages, + Values = produtos + }; + } public async Task> GetProdutosMaisVendidosAsync() { @@ -38,4 +85,46 @@ public async Task> GetProdutosMaisVendidosAsync() .Where(x => produtosIds.Contains(x.Id)) .ToListAsync(); } + + public async Task GetTotalPageProdutosAsync() + { + return await _parceiroContext + .Produtos + .AsQueryable() + .TotalPage(_take); + } + + public async Task> GetProdutosByCategoriaIdAsync(Guid categoriaId) + { + var produtos = await _parceiroContext + .Produtos + .AsNoTracking() + .AsQueryable() + .OrderBy(x => x.Numero) + .Include(x => x.Categoria) + .Where(x => x.CategoriaId == categoriaId) + .ToListAsync(); + + var tamanhos = await _parceiroContext + .TamanhosProdutos + .AsNoTracking() + .Include(x => x.Tamanho) + .ToListAsync(); + + if (produtos.Count > 0) + { + produtos.ForEach(produto => + { + produto.Categoria.Produtos = new(); + produto.Tamanhos = tamanhos + .Where(x => x.ProdutoId == produto.Id) + .Select(tm => + new Tamanho(tm.Tamanho.Id, tm.Tamanho.DataDeCriacao, tm.Tamanho.DataDeAtualizacao, tm.Tamanho.Numero, tm.Tamanho.Descricao) + ) + .ToList(); + }); + } + + return produtos; + } } diff --git a/OpenAdm.IoC/DependencyInjectRepositories.cs b/OpenAdm.IoC/DependencyInjectRepositories.cs index 82111f5..4538d15 100644 --- a/OpenAdm.IoC/DependencyInjectRepositories.cs +++ b/OpenAdm.IoC/DependencyInjectRepositories.cs @@ -1,6 +1,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using OpenAdm.Domain.Interfaces; +using OpenAdm.Infra.Cached.Cached; +using OpenAdm.Infra.Cached.Interfaces; +using OpenAdm.Infra.Cached.Services; using OpenAdm.Infra.Factories.Factory; using OpenAdm.Infra.Factories.Interfaces; using OpenAdm.Infra.Repositories; @@ -10,17 +13,29 @@ namespace OpenAdm.IoC; public static class DependencyInjectRepositories { - public static void InjectRepositories(this IServiceCollection services) + public static void InjectRepositories(this IServiceCollection services, string connectionString) { + services.AddStackExchangeRedisCache(options => + { + options.Configuration = connectionString; + }); + services.AddTransient(typeof(ICachedService<>), typeof(CachedService<>)); + services.AddTransient(); services.AddTransient(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); } } diff --git a/OpenAdm.IoC/DependencyInjectyApplication.cs b/OpenAdm.IoC/DependencyInjectyApplication.cs index 6d93bdf..1dc1a9b 100644 --- a/OpenAdm.IoC/DependencyInjectyApplication.cs +++ b/OpenAdm.IoC/DependencyInjectyApplication.cs @@ -13,5 +13,7 @@ public static void InjectServices(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } diff --git a/OpenAdm.IoC/OpenAdm.IoC.csproj b/OpenAdm.IoC/OpenAdm.IoC.csproj index 2952542..2b902a9 100644 --- a/OpenAdm.IoC/OpenAdm.IoC.csproj +++ b/OpenAdm.IoC/OpenAdm.IoC.csproj @@ -7,6 +7,7 @@ +