├── .github └── workflows │ └── dotnet-core.yml ├── .gitignore ├── Apresentação └── Aplicando design patterns na prática com C#.pdf ├── README.md ├── src ├── Application │ ├── Application.csproj │ ├── DTO │ │ ├── DebitoVeiculo.cs │ │ └── Veiculo.cs │ ├── Decorators │ │ ├── DetranVerificadorDebitosDecoratorCache.cs │ │ └── DetranVerificadorDebitosDecoratorLogger.cs │ ├── Implementations │ │ └── DetranVerificadorDebitosServices.cs │ ├── Repository │ │ ├── IDetranVerificadorDebitosFactory.cs │ │ └── IDetranVerificadorDebitosRepository.cs │ └── Services │ │ └── IDetranVerificadorDebitosService.cs ├── DesignPatternSamples.sln ├── Domain │ ├── Domain.csproj │ └── Repository │ │ └── IDetranVerificadorDebitos.cs ├── Infra.Repository.Detran.Tests │ ├── DependencyInjectionFixture.cs │ ├── DetranVerificadorDebitosFactoryTests.cs │ ├── Infra.Repository.Detran.Tests.csproj │ └── appsettings.json ├── Infra.Repository.Detran │ ├── DetranPEVerificadorDebitosRepository.cs │ ├── DetranRJVerificadorDebitosRepository.cs │ ├── DetranRSVerificadorDebitosRepository.cs │ ├── DetranSPVerificadorDebitosRepository.cs │ ├── DetranVerificadorDebitosFactory.cs │ ├── DetranVerificadorDebitosRepositoryCrawlerBase.cs │ └── Infra.Repository.Detran.csproj ├── WebAPI │ ├── Controllers │ │ └── Detran │ │ │ └── DebitosController.cs │ ├── Mapper │ │ └── DetranMapper.cs │ ├── Middlewares │ │ └── ExceptionHandlingMiddleware.cs │ ├── Models │ │ ├── AbstractResultModel.cs │ │ ├── Detran │ │ │ ├── DebitoVeiculoModel.cs │ │ │ └── VeiculoModel.cs │ │ ├── FailureResultModel.cs │ │ ├── IResultModel.cs │ │ ├── ResultDetail.cs │ │ └── SuccessResultModel.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── WebAPI.csproj │ └── appsettings.json ├── Workbench.Comparer │ ├── GenericComparerFactory.cs │ └── Workbench.Comparer.csproj ├── Workbench.DependencyInjection.Extensions │ ├── ServiceCollectionExtensions.cs │ └── Workbench.DependencyInjection.Extensions.csproj ├── Workbench.GenericComparer.Tests │ ├── GenericComparerFactoryTest.cs │ ├── Workbench.GenericComparer.Tests.csproj │ └── coverage.json ├── Workbench.IDistributedCache.Extensions │ ├── IDistributedCacheExtensions.cs │ └── Workbench.IDistributedCache.Extensions.csproj ├── Workbench.IFormatter.Extensions.Tests │ ├── IFormaterExtensionsTests.cs │ └── Workbench.IFormatter.Extensions.Tests.csproj ├── Workbench.IFormatter.Extensions │ ├── IFormatterExtensions.cs │ └── Workbench.IFormatter.Extensions.csproj ├── Workbench.Linq.Extensions.Tests │ ├── DistinctExtensionsTests.cs │ └── Workbench.Linq.Extensions.Tests.csproj └── Workbench.Linq.Extensions │ ├── DistinctExtensions.cs │ └── Workbench.Linq.Extensions.csproj └── test-coverage.bat /.github/workflows/dotnet-core.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: 4 | push: 5 | branches: [ main, develop ] 6 | pull_request: [ main ] 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | env: 12 | working-directory: ./src 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup .NET Core 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 3.1.301 21 | 22 | - name: Install dependencies 23 | working-directory: ${{env.working-directory}} 24 | run: dotnet restore 25 | 26 | - name: Build 27 | working-directory: ${{env.working-directory}} 28 | run: dotnet build --configuration Release --no-restore 29 | 30 | - name: Test 31 | working-directory: ${{env.working-directory}} 32 | run: dotnet test --no-restore --verbosity normal -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2 | .vs/ 3 | [Bb]in/ 4 | [Oo]bj/ 5 | *.user 6 | TestResults/ 7 | 8 | # VS Code 9 | .vscode/ 10 | 11 | # Testes de Cobertura 12 | CoverageResults 13 | 14 | # Sonar 15 | .sonarqube 16 | test-coverage-with-sonar.bat -------------------------------------------------------------------------------- /Apresentação/Aplicando design patterns na prática com C#.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fructuoso/DesignPatternSamples/7eafc0492b3e8dd89c4492f763a56a35887d8c01/Apresentação/Aplicando design patterns na prática com C#.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DesignPatternSamples 2 | |Branch|Build| 3 | |-:|-| 4 | |Develop|![.NET Core](https://github.com/fructuoso/DesignPatternSamples/workflows/.NET%20Core/badge.svg?branch=develop)| 5 | |Main|![.NET Core](https://github.com/fructuoso/DesignPatternSamples/workflows/.NET%20Core/badge.svg?branch=main)| 6 | 7 | Aplicação de exemplo de aplicação de Design Patterns na prática em um projeto WebAPI .NET Core 3.1 utilizada na palestra "Aplicando design patterns na prática com C#" ([Link Apresentação](Apresenta%C3%A7%C3%A3o/Aplicando%20design%20patterns%20na%20pr%C3%A1tica%20com%20C%23.pdf)) 8 | 9 | ## Testes de Cobertura 10 | 11 | Passo a passo sobre como executar os testes unitários (e calcular o code coverage) localmente antes de realizar o commit. 12 | 13 | Obs.: O VS2019 possui esta funcionalidade nativamente, porém ela só está habilitada para a versão Enterprise segundo a [documentação](https://docs.microsoft.com/pt-br/visualstudio/test/using-code-coverage-to-determine-how-much-code-is-being-tested?view=vs-2019) da própria Microsoft. 14 | 15 | ### Pré-Requisitos 16 | 17 | Para gerar o relatório é necessário instalar o **dotnet-reportgenerator-globaltool** 18 | 19 | ```script 20 | dotnet tool install --global dotnet-reportgenerator-globaltool --version 4.6.1 21 | ```` 22 | 23 | ### Execução 24 | 25 | Executar o **.bat** para realizar a execução dos testes automatizados com a extração do relatório de cobertura na sequência. 26 | 27 | ```bat 28 | $ test-coverage.bat 29 | ``` 30 | 31 | ## Padrões na Prática 32 | 33 | ### Strategy 34 | 35 | #### Problema: 36 | 37 | Nosso objetivo é Utilizar o método Distinct do System.Linq, este por sua vez espera como entrada uma IEqualityComparer. Isso por si só já representa uma implementação de Strategy, entretanto nós não queremos criar uma única implementação engessada que nos permita comparar um determinado objeto de uma única forma. 38 | 39 | ##### Solução: 40 | 41 | 1. Criar uma classe que implemente a interface [IEqualityComparer](https://docs.microsoft.com/pt-br/dotnet/api/system.collections.generic.iequalitycomparer-1?view=netcore-3.1); 42 | 2. Esta classe deve receber o 'como' os objetos deverão ser comparados através de um parâmetro, que neste caso é uma função anônima; 43 | 44 | Desta forma a classe que criamos sabe comparar objetos, porém ela não sabe os critérios que serão utilizados, os critérios serão injetados através de uma função anônima. 45 | 46 | [Implementação](src/Workbench.Comparer/GenericComparerFactory.cs)\ 47 | [Consumo](src/Workbench.GenericComparer.Tests/GenericComparerFactoryTest.cs#L27) 48 | 49 | Podemos tornar o consumo ainda mais interessante criando uma *Sugar Syntax* através de métodos de extensão. 50 | 51 | [Implementação](src/Workbench.Linq.Extensions/DistinctExtensions.cs)\ 52 | [Consumo](src/Workbench.Linq.Extensions.Tests/DistinctExtensionsTests.cs#L26) 53 | 54 | Desta forma através do padrão [Strategy](#strategy) estamos aderentes ao princípio **Aberto-Fechado** e **Inversão de Controle**. 55 | 56 | ### Factory 57 | 58 | #### Problema: 59 | 60 | Nós queremos criar um serviço de consulta de débitos do veículo que seja capaz de acessar o sistema do DETRAN e obter estas informações, porém o DETRAN possui uma aplicação completamente diferente de acordo com o estado. 61 | 62 | Nós não queremos modificar o nosso serviço sempre que um novo estado for implementado. 63 | 64 | #### Solução: 65 | 66 | 1. Criar uma interface que determine uma assinatura única para o serviço; 67 | 2. Realizar uma implementação para cada um dos estados; 68 | 3. Criar uma classe Factory, onde sua responsabilidade será determinar qual classe concreta deverá ser instanciada; 69 | 70 | [Implementação](src/Infra.Repository.Detran/DetranVerificadorDebitosFactory.cs)\ 71 | [Consumo](src/Application/Implementations/DetranVerificadorDebitosServices.cs#L20)\ 72 | [Teste](src/Infra.Repository.Detran.Tests/DetranVerificadorDebitosFactoryTests.cs#L22) 73 | 74 | Desta forma através do padrão [Factory](#factory) estamos aderentes ao princípio **Aberto-Fechado**. 75 | 76 | Neste exemplo o nosso [Factory](#factory) ainda está diretamente relacionado ao princípio **Substituição de Liskov**. 77 | 78 | ### Singleton 79 | 80 | #### Problema: 81 | 82 | Visto que o nosso Factory tem como responsabilidade apenas identificar a classe concreta que teve ser inicializada a partir de um Setup pré-estabelecido no [Startup](src/WebAPI/Startup.cs#L130) da aplicação, não faz sentido que ele seja instanciado a cada solicitação. 83 | 84 | #### Solução: 85 | 86 | Como estamos fazendo uso da Injeção de Dependência nativa do .Net Core processo se torna mais simples: 87 | 88 | 1. Modificar o registro no Startup para que o serviço seja registrado como Singleton. 89 | 90 | [Implementação](src/WebAPI/Startup.cs#L111) 91 | 92 | Com isso nós temos uma única instância sendo inicializada e configurada no [Startup](src/WebAPI/Startup.cs#L130) da aplicação. 93 | 94 | ### Template Method 95 | 96 | #### Problema: 97 | 98 | Visto que em algum momento iremos implementar 27 serviços diferentes para acessar os DETRAN que temos espalhados pelo Brasil. 99 | 100 | Entendemos que, mesmo que, os sites sejam diferentes, os passos necessários para extrair a informação costumam ser semelhantes: 101 | 102 | * Consultar Site 103 | * Consolidar Resultado 104 | 105 | Como a nossa interface [IDetranVerificadorDebitosRepository](src/Application/Repository/IDetranVerificadorDebitosRepository.cs) possui apenas o método ConsultarDebitos, nosso código corre risco de não ficar padronizado e ainda perdermos o princípio da **Responsabilidade Única**. 106 | 107 | #### Solução: 108 | 109 | 1. Criar uma classe abstrata com métodos mais específicos para realizar o trabalho desejado; 110 | 2. A classe abstrata 'deve' implementar o método exposto pela Interface; 111 | 3. Ao invés das classes implementarem a Interface, elas herdarão o comportamento da classe abstrata, implementando apenas os métodos mais específicos. 112 | 113 | Com isso torna-se mais fácil: 114 | * Dividir o trabalho; 115 | * Testar o código. 116 | 117 | [Implementação](src/Infra.Repository.Detran/DetranVerificadorDebitosRepositoryCrawlerBase.cs)\ 118 | [Consumo](src/Infra.repository.detran/DetranPEVerificadorDebitosRepository.cs) 119 | 120 | O neste exemplo o nosso [Template Method](#template-method) ainda seguindo o princípio **Segregação da Interface**, onde os métodos específicos foram adicionados na nossa classe abstrata [DetranVerificadorDebitosRepositoryCrawlerBase](src/Repository.Detran/../Infra.Repository.Detran/DetranVerificadorDebitosRepositoryCrawlerBase.cs), desta forma conseguimos atingir também o princípio de **Substituição de Liskov**. 121 | 122 | ### Decorator 123 | 124 | #### Problema: 125 | 126 | Com o serviço [DetranVerificadorDebitosServices](src/Application/Implementations/DetranVerificadorDebitosServices.cs) identificamos que precisamos adicionar funcionalidades técnicas a ele (como por exemplo **Log** e **Cache**), porém essas funcionalidades não devem gerar acoplamento no nosso código. 127 | 128 | Então como fazer isso sem quebrar os princípios **Responsabilidade Única** e **Aberto-Fechado**? 129 | 130 | #### Solução: 131 | 132 | Neste cenário estamos usando uma abordagem que nos permite transferir a complexidade de registrar um Decorator no ServiceCollection para um método de extensão. 133 | 134 | Desta forma precisamos: 135 | 136 | 1. Criar uma nova classe concreta que deverá implementar a Interface que será 'decorada'; 137 | 2. Implementar nesta nova classe a funcionalidade que gostaríamos de acrescentar ao método em questão; 138 | 3. Adicionar Decorator no Injetor de Dependências fazendo referência à interface que será decorada. 139 | 140 | Obs.: É possível incluir mais de um Decorator, porém é preciso ter ciência de que a ordem em que eles são associados faz diferença no resultado final. 141 | 142 | [Método de Extensão](src/Workbench.DependencyInjection.Extensions/ServiceCollectionExtensions.cs#L10)\ 143 | [Implementação](src/Application/Decorators/DetranVerificadorDebitosDecoratorLogger.cs#L23)\ 144 | [Registro](src/WebAPI/Startup.cs#L110) 145 | 146 | O Decorator funciona como uma 'Boneca Russa' dessa forma podemos 'empilhar' diversos Decorators em uma mesma Interface. 147 | 148 | Temos o exemplo de um segundo Decorator adicionando o recurso de Cache ao nosso Service. 149 | 150 | [Implementação](src/Application/Decorators/DetranVerificadorDebitosDecoratorCache.cs#L25)\ 151 | [Registro](src/WebAPI/Startup.cs#L09) 152 | 153 | Desta forma nós agregamos duas funcionalidades ao nosso serviço sem modificar o comportamento do serviço, ou modificar quem chama o serviço, desta forma estamos aderentes aos princípios **Responsabilidade Única**, **Aberto-Fechado** e **Inversão de Controle**. 154 | 155 | Obs.: Seguir o princípio Segregação de Interfaces pode tornar o seu Decorator mais simples de ser implementado, visto que você terá menos métodos para submeter ao padrão. -------------------------------------------------------------------------------- /src/Application/Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | DesignPatternSamples.Application 6 | DesignPatternSamples.Application 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Application/DTO/DebitoVeiculo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DesignPatternSamples.Application.DTO 4 | { 5 | [Serializable] 6 | public class DebitoVeiculo 7 | { 8 | public DateTime DataOcorrencia { get; set; } 9 | public string Descricao { get; set; } 10 | public double Valor { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Application/DTO/Veiculo.cs: -------------------------------------------------------------------------------- 1 | namespace DesignPatternSamples.Application.DTO 2 | { 3 | public class Veiculo 4 | { 5 | public string Placa { get; set; } 6 | public string UF { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Application/Decorators/DetranVerificadorDebitosDecoratorCache.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using DesignPatternSamples.Application.Services; 3 | using Microsoft.Extensions.Caching.Distributed; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Workbench.IDistributedCache.Extensions; 7 | 8 | namespace DesignPatternSamples.Application.Decorators 9 | { 10 | public class DetranVerificadorDebitosDecoratorCache : IDetranVerificadorDebitosService 11 | { 12 | private readonly IDetranVerificadorDebitosService _Inner; 13 | private readonly IDistributedCache _Cache; 14 | 15 | private const int DUCACAO_CACHE = 20; 16 | 17 | public DetranVerificadorDebitosDecoratorCache( 18 | IDetranVerificadorDebitosService inner, 19 | IDistributedCache cache) 20 | { 21 | _Inner = inner; 22 | _Cache = cache; 23 | } 24 | 25 | public Task> ConsultarDebitos(Veiculo veiculo) 26 | { 27 | return _Cache.GetOrCreateAsync($"{veiculo.UF}_{veiculo.Placa}", () => _Inner.ConsultarDebitos(veiculo), DUCACAO_CACHE); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Application/Decorators/DetranVerificadorDebitosDecoratorLogger.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using DesignPatternSamples.Application.Services; 3 | using Microsoft.Extensions.Logging; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading.Tasks; 7 | 8 | namespace DesignPatternSamples.Application.Decorators 9 | { 10 | public class DetranVerificadorDebitosDecoratorLogger : IDetranVerificadorDebitosService 11 | { 12 | private readonly IDetranVerificadorDebitosService _Inner; 13 | private readonly ILogger _Logger; 14 | 15 | public DetranVerificadorDebitosDecoratorLogger( 16 | IDetranVerificadorDebitosService inner, 17 | ILogger logger) 18 | { 19 | _Inner = inner; 20 | _Logger = logger; 21 | } 22 | 23 | public async Task> ConsultarDebitos(Veiculo veiculo) 24 | { 25 | Stopwatch watch = Stopwatch.StartNew(); 26 | _Logger.LogInformation($"Iniciando a execução do método ConsultarDebitos({veiculo})"); 27 | var result = await _Inner.ConsultarDebitos(veiculo); 28 | watch.Stop(); 29 | _Logger.LogInformation($"Encerrando a execução do método ConsultarDebitos({veiculo}) {watch.ElapsedMilliseconds}ms"); 30 | return result; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Application/Implementations/DetranVerificadorDebitosServices.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using DesignPatternSamples.Application.Repository; 3 | using DesignPatternSamples.Application.Services; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace DesignPatternSamples.Application.Implementations 8 | { 9 | public class DetranVerificadorDebitosServices : IDetranVerificadorDebitosService 10 | { 11 | private readonly IDetranVerificadorDebitosFactory _Factory; 12 | 13 | public DetranVerificadorDebitosServices(IDetranVerificadorDebitosFactory factory) 14 | { 15 | _Factory = factory; 16 | } 17 | 18 | public Task> ConsultarDebitos(Veiculo veiculo) 19 | { 20 | IDetranVerificadorDebitosRepository repository = _Factory.Create(veiculo.UF); 21 | return repository.ConsultarDebitos(veiculo); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Application/Repository/IDetranVerificadorDebitosFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DesignPatternSamples.Application.Repository 4 | { 5 | public interface IDetranVerificadorDebitosFactory 6 | { 7 | public IDetranVerificadorDebitosFactory Register(string UF, Type repository); 8 | public IDetranVerificadorDebitosRepository Create(string UF); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Application/Repository/IDetranVerificadorDebitosRepository.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace DesignPatternSamples.Application.Repository 6 | { 7 | public interface IDetranVerificadorDebitosRepository 8 | { 9 | Task> ConsultarDebitos(Veiculo veiculo); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Application/Services/IDetranVerificadorDebitosService.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace DesignPatternSamples.Application.Services 6 | { 7 | public interface IDetranVerificadorDebitosService 8 | { 9 | Task> ConsultarDebitos(Veiculo veiculo); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/DesignPatternSamples.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30523.141 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebAPI", "WebAPI\WebAPI.csproj", "{7BB26546-C696-4A49-BB57-AFFA5F2B6799}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01. Presentation", "01. Presentation", "{8B43F163-AFA9-431D-8B34-13880AC45FEA}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "00. Framework", "00. Framework", "{81650748-EE14-4EA6-A5FC-D8CCEB7873D5}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workbench.Comparer", "Workbench.Comparer\Workbench.Comparer.csproj", "{0F78222C-8493-4293-A424-33ADB447CC0C}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workbench.GenericComparer.Tests", "Workbench.GenericComparer.Tests\Workbench.GenericComparer.Tests.csproj", "{4467CDE6-BAC4-4A26-80B7-F0D675971830}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workbench.Linq.Extensions", "Workbench.Linq.Extensions\Workbench.Linq.Extensions.csproj", "{E6B214ED-0F85-4D4C-B972-0F81C9F9CB2E}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workbench.Linq.Extensions.Tests", "Workbench.Linq.Extensions.Tests\Workbench.Linq.Extensions.Tests.csproj", "{D2A54E5D-50DC-44F0-974C-8EC22ADAA064}" 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04. Infra", "04. Infra", "{42A88419-8568-433D-A027-A5853780A83E}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infra.Repository.Detran.Tests", "Infra.Repository.Detran.Tests\Infra.Repository.Detran.Tests.csproj", "{660A2898-16BB-4B50-944C-3D6E06C51646}" 23 | EndProject 24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02. Application", "02. Application", "{FAAF7A15-4496-4D73-9D1A-581C3B230232}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "Application\Application.csproj", "{F18BCE3C-E182-4C8F-A39C-7CEA4853CE73}" 27 | EndProject 28 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infra.Repository.Detran", "Infra.Repository.Detran\Infra.Repository.Detran.csproj", "{3EEE90D9-A054-400C-BFC7-9865C0C2DEE5}" 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workbench.DependencyInjection.Extensions", "Workbench.DependencyInjection.Extensions\Workbench.DependencyInjection.Extensions.csproj", "{E6959B8C-EB09-4288-B84C-8752DD708F41}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workbench.IDistributedCache.Extensions", "Workbench.IDistributedCache.Extensions\Workbench.IDistributedCache.Extensions.csproj", "{FD7E1EE8-EF31-4785-B945-1ACDDB5D065F}" 33 | EndProject 34 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workbench.IFormatter.Extensions", "Workbench.IFormatter.Extensions\Workbench.IFormatter.Extensions.csproj", "{C08D47C6-A1B0-4680-8854-AA5E20C91415}" 35 | EndProject 36 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workbench.IFormatter.Extensions.Tests", "Workbench.IFormatter.Extensions.Tests\Workbench.IFormatter.Extensions.Tests.csproj", "{95C404BB-1BDD-494D-816B-49F3B260C1DC}" 37 | EndProject 38 | Global 39 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 40 | Debug|Any CPU = Debug|Any CPU 41 | Release|Any CPU = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 44 | {7BB26546-C696-4A49-BB57-AFFA5F2B6799}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {7BB26546-C696-4A49-BB57-AFFA5F2B6799}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {7BB26546-C696-4A49-BB57-AFFA5F2B6799}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {7BB26546-C696-4A49-BB57-AFFA5F2B6799}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {0F78222C-8493-4293-A424-33ADB447CC0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {0F78222C-8493-4293-A424-33ADB447CC0C}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {0F78222C-8493-4293-A424-33ADB447CC0C}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {0F78222C-8493-4293-A424-33ADB447CC0C}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {4467CDE6-BAC4-4A26-80B7-F0D675971830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {4467CDE6-BAC4-4A26-80B7-F0D675971830}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {4467CDE6-BAC4-4A26-80B7-F0D675971830}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {4467CDE6-BAC4-4A26-80B7-F0D675971830}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {E6B214ED-0F85-4D4C-B972-0F81C9F9CB2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {E6B214ED-0F85-4D4C-B972-0F81C9F9CB2E}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {E6B214ED-0F85-4D4C-B972-0F81C9F9CB2E}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {E6B214ED-0F85-4D4C-B972-0F81C9F9CB2E}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {D2A54E5D-50DC-44F0-974C-8EC22ADAA064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {D2A54E5D-50DC-44F0-974C-8EC22ADAA064}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {D2A54E5D-50DC-44F0-974C-8EC22ADAA064}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {D2A54E5D-50DC-44F0-974C-8EC22ADAA064}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {660A2898-16BB-4B50-944C-3D6E06C51646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {660A2898-16BB-4B50-944C-3D6E06C51646}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {660A2898-16BB-4B50-944C-3D6E06C51646}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {660A2898-16BB-4B50-944C-3D6E06C51646}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {F18BCE3C-E182-4C8F-A39C-7CEA4853CE73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {F18BCE3C-E182-4C8F-A39C-7CEA4853CE73}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {F18BCE3C-E182-4C8F-A39C-7CEA4853CE73}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {F18BCE3C-E182-4C8F-A39C-7CEA4853CE73}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {3EEE90D9-A054-400C-BFC7-9865C0C2DEE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {3EEE90D9-A054-400C-BFC7-9865C0C2DEE5}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {3EEE90D9-A054-400C-BFC7-9865C0C2DEE5}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {3EEE90D9-A054-400C-BFC7-9865C0C2DEE5}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {E6959B8C-EB09-4288-B84C-8752DD708F41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {E6959B8C-EB09-4288-B84C-8752DD708F41}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {E6959B8C-EB09-4288-B84C-8752DD708F41}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {E6959B8C-EB09-4288-B84C-8752DD708F41}.Release|Any CPU.Build.0 = Release|Any CPU 80 | {FD7E1EE8-EF31-4785-B945-1ACDDB5D065F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 81 | {FD7E1EE8-EF31-4785-B945-1ACDDB5D065F}.Debug|Any CPU.Build.0 = Debug|Any CPU 82 | {FD7E1EE8-EF31-4785-B945-1ACDDB5D065F}.Release|Any CPU.ActiveCfg = Release|Any CPU 83 | {FD7E1EE8-EF31-4785-B945-1ACDDB5D065F}.Release|Any CPU.Build.0 = Release|Any CPU 84 | {C08D47C6-A1B0-4680-8854-AA5E20C91415}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 85 | {C08D47C6-A1B0-4680-8854-AA5E20C91415}.Debug|Any CPU.Build.0 = Debug|Any CPU 86 | {C08D47C6-A1B0-4680-8854-AA5E20C91415}.Release|Any CPU.ActiveCfg = Release|Any CPU 87 | {C08D47C6-A1B0-4680-8854-AA5E20C91415}.Release|Any CPU.Build.0 = Release|Any CPU 88 | {95C404BB-1BDD-494D-816B-49F3B260C1DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 89 | {95C404BB-1BDD-494D-816B-49F3B260C1DC}.Debug|Any CPU.Build.0 = Debug|Any CPU 90 | {95C404BB-1BDD-494D-816B-49F3B260C1DC}.Release|Any CPU.ActiveCfg = Release|Any CPU 91 | {95C404BB-1BDD-494D-816B-49F3B260C1DC}.Release|Any CPU.Build.0 = Release|Any CPU 92 | EndGlobalSection 93 | GlobalSection(SolutionProperties) = preSolution 94 | HideSolutionNode = FALSE 95 | EndGlobalSection 96 | GlobalSection(NestedProjects) = preSolution 97 | {7BB26546-C696-4A49-BB57-AFFA5F2B6799} = {8B43F163-AFA9-431D-8B34-13880AC45FEA} 98 | {0F78222C-8493-4293-A424-33ADB447CC0C} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 99 | {4467CDE6-BAC4-4A26-80B7-F0D675971830} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 100 | {E6B214ED-0F85-4D4C-B972-0F81C9F9CB2E} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 101 | {D2A54E5D-50DC-44F0-974C-8EC22ADAA064} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 102 | {660A2898-16BB-4B50-944C-3D6E06C51646} = {42A88419-8568-433D-A027-A5853780A83E} 103 | {F18BCE3C-E182-4C8F-A39C-7CEA4853CE73} = {FAAF7A15-4496-4D73-9D1A-581C3B230232} 104 | {3EEE90D9-A054-400C-BFC7-9865C0C2DEE5} = {42A88419-8568-433D-A027-A5853780A83E} 105 | {E6959B8C-EB09-4288-B84C-8752DD708F41} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 106 | {FD7E1EE8-EF31-4785-B945-1ACDDB5D065F} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 107 | {C08D47C6-A1B0-4680-8854-AA5E20C91415} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 108 | {95C404BB-1BDD-494D-816B-49F3B260C1DC} = {81650748-EE14-4EA6-A5FC-D8CCEB7873D5} 109 | EndGlobalSection 110 | GlobalSection(ExtensibilityGlobals) = postSolution 111 | SolutionGuid = {4A90C331-830A-4D9E-971D-F17899C6CD14} 112 | EndGlobalSection 113 | EndGlobal 114 | -------------------------------------------------------------------------------- /src/Domain/Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | DesignPatternSamples.Domain 6 | DesignPatternSamples.Domain 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Domain/Repository/IDetranVerificadorDebitos.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace DesignPatternSamples.Domain.Repository.Detran 4 | { 5 | public interface IDetranVerificadorDebitos 6 | { 7 | Task ConsultarDebitos(Veiculo veiculo); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran.Tests/DependencyInjectionFixture.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.Repository; 2 | using DesignPatternSamples.Infra.Repository.Detran; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System; 6 | 7 | namespace DesignPatternsSamples.Infra.Repository.Detran.Tests 8 | { 9 | public class DependencyInjectionFixture 10 | { 11 | public readonly IServiceProvider ServiceProvider; 12 | 13 | public DependencyInjectionFixture() 14 | { 15 | var services = new ServiceCollection() 16 | .AddLogging() 17 | .AddTransient() 18 | .AddTransient() 19 | .AddTransient() 20 | .AddTransient() 21 | .AddSingleton(); 22 | 23 | #region IConfiguration 24 | services.AddTransient((services) => 25 | new ConfigurationBuilder() 26 | 27 | .SetBasePath(AppContext.BaseDirectory) 28 | .AddJsonFile("appsettings.json", false, true) 29 | .Build() 30 | ); 31 | #endregion 32 | 33 | ServiceProvider = services.BuildServiceProvider(); 34 | 35 | ServiceProvider.GetService() 36 | .Register("PE", typeof(DetranPEVerificadorDebitosRepository)) 37 | .Register("RJ", typeof(DetranRJVerificadorDebitosRepository)) 38 | .Register("SP", typeof(DetranSPVerificadorDebitosRepository)) 39 | .Register("RS", typeof(DetranRSVerificadorDebitosRepository)); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Infra.Repository.Detran.Tests/DetranVerificadorDebitosFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.Repository; 2 | using DesignPatternSamples.Infra.Repository.Detran; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using Xunit; 6 | 7 | namespace DesignPatternsSamples.Infra.Repository.Detran.Tests 8 | { 9 | public class DetranVerificadorDebitosFactoryTests : IClassFixture 10 | { 11 | private readonly IDetranVerificadorDebitosFactory _Factory; 12 | 13 | public DetranVerificadorDebitosFactoryTests(DependencyInjectionFixture dependencyInjectionFixture) 14 | { 15 | var serviceProvider = dependencyInjectionFixture.ServiceProvider; 16 | _Factory = serviceProvider.GetService(); 17 | } 18 | 19 | [Theory(DisplayName = "Dado um UF que está devidamente registrado no Factory devemos receber a sua implementação correspondente")] 20 | [InlineData("PE", typeof(DetranPEVerificadorDebitosRepository))] 21 | [InlineData("SP", typeof(DetranSPVerificadorDebitosRepository))] 22 | [InlineData("RJ", typeof(DetranRJVerificadorDebitosRepository))] 23 | [InlineData("RS", typeof(DetranRSVerificadorDebitosRepository))] 24 | public void InstanciarServicoPorUFRegistrado(string uf, Type implementacao) 25 | { 26 | var resultado = _Factory.Create(uf); 27 | 28 | Assert.NotNull(resultado); 29 | Assert.IsType(implementacao, resultado); 30 | } 31 | 32 | [Fact(DisplayName = "Dado um UF que não está registrado no Factory devemos receber NULL")] 33 | public void InstanciarServicoPorUFNaoRegistrado() 34 | { 35 | IDetranVerificadorDebitosRepository implementacao = _Factory.Create("CE"); 36 | 37 | Assert.Null(implementacao); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran.Tests/Infra.Repository.Detran.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | PreserveNewest 16 | true 17 | PreserveNewest 18 | 19 | 20 | 21 | 22 | 23 | all 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | all 36 | runtime; build; native; contentfiles; analyzers; buildtransitive 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran.Tests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Trace", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran/DetranPEVerificadorDebitosRepository.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace DesignPatternSamples.Infra.Repository.Detran 8 | { 9 | public class DetranPEVerificadorDebitosRepository : DetranVerificadorDebitosRepositoryCrawlerBase 10 | { 11 | private readonly ILogger _Logger; 12 | 13 | public DetranPEVerificadorDebitosRepository(ILogger logger) 14 | { 15 | _Logger = logger; 16 | } 17 | 18 | protected override Task> PadronizarResultado(string html) 19 | { 20 | _Logger.LogDebug($"Padronizando o Resultado {html}."); 21 | return Task.FromResult>(new List() { new DebitoVeiculo() { DataOcorrencia = DateTime.UtcNow } }); 22 | } 23 | 24 | protected override Task RealizarAcesso(Veiculo veiculo) 25 | { 26 | Task.Delay(5000).Wait(); //Deixando o serviço mais lento para evidenciar o uso do CACHE. 27 | _Logger.LogDebug($"Consultando débitos do veículo placa {veiculo.Placa} para o estado de PE."); 28 | return Task.FromResult("CONTEUDO DO SITE DO DETRAN/PE"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran/DetranRJVerificadorDebitosRepository.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using Microsoft.Extensions.Logging; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace DesignPatternSamples.Infra.Repository.Detran 7 | { 8 | public class DetranRJVerificadorDebitosRepository : DetranVerificadorDebitosRepositoryCrawlerBase 9 | { 10 | private readonly ILogger _Logger; 11 | 12 | public DetranRJVerificadorDebitosRepository(ILogger logger) 13 | { 14 | _Logger = logger; 15 | } 16 | 17 | protected override Task> PadronizarResultado(string html) 18 | { 19 | _Logger.LogDebug($"Padronizando o Resultado {html}."); 20 | return Task.FromResult>(new List() { new DebitoVeiculo() }); 21 | } 22 | 23 | protected override Task RealizarAcesso(Veiculo veiculo) 24 | { 25 | _Logger.LogDebug($"Consultando débitos do veículo placa {veiculo.Placa} para o estado de RJ."); 26 | return Task.FromResult("CONTEUDO DO SITE DO DETRAN/RJ"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran/DetranRSVerificadorDebitosRepository.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using Microsoft.Extensions.Logging; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace DesignPatternSamples.Infra.Repository.Detran 7 | { 8 | public class DetranRSVerificadorDebitosRepository : DetranVerificadorDebitosRepositoryCrawlerBase 9 | { 10 | private readonly ILogger _Logger; 11 | 12 | public DetranRSVerificadorDebitosRepository(ILogger logger) 13 | { 14 | _Logger = logger; 15 | } 16 | 17 | protected override Task> PadronizarResultado(string html) 18 | { 19 | _Logger.LogDebug($"Padronizando o Resultado {html}."); 20 | return Task.FromResult>(new List() { new DebitoVeiculo() }); 21 | } 22 | 23 | protected override Task RealizarAcesso(Veiculo veiculo) 24 | { 25 | _Logger.LogDebug($"Consultando débitos do veículo placa {veiculo.Placa} para o estado de RS."); 26 | return Task.FromResult("CONTEUDO DO SITE DO DETRAN/RS"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran/DetranSPVerificadorDebitosRepository.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using DesignPatternSamples.Application.Repository; 3 | using Microsoft.Extensions.Logging; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace DesignPatternSamples.Infra.Repository.Detran 8 | { 9 | public class DetranSPVerificadorDebitosRepository : IDetranVerificadorDebitosRepository 10 | { 11 | private readonly ILogger _Logger; 12 | 13 | public DetranSPVerificadorDebitosRepository(ILogger logger) 14 | { 15 | _Logger = logger; 16 | } 17 | 18 | public Task> ConsultarDebitos(Veiculo veiculo) 19 | { 20 | _Logger.LogDebug($"Consultando débitos do veículo placa {veiculo.Placa} para o estado de SP."); 21 | return Task.FromResult>(new List() { new DebitoVeiculo() }); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran/DetranVerificadorDebitosFactory.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.Repository; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace DesignPatternSamples.Infra.Repository.Detran 6 | { 7 | public class DetranVerificadorDebitosFactory : IDetranVerificadorDebitosFactory 8 | { 9 | private readonly IServiceProvider _ServiceProvider; 10 | private readonly IDictionary _Repositories = new Dictionary(); 11 | 12 | public DetranVerificadorDebitosFactory(IServiceProvider serviceProvider) 13 | { 14 | _ServiceProvider = serviceProvider; 15 | } 16 | 17 | public IDetranVerificadorDebitosRepository Create(string UF) 18 | { 19 | IDetranVerificadorDebitosRepository result = null; 20 | 21 | if (_Repositories.TryGetValue(UF, out Type type)) 22 | { 23 | result = _ServiceProvider.GetService(type) as IDetranVerificadorDebitosRepository; 24 | } 25 | 26 | return result; 27 | } 28 | 29 | public IDetranVerificadorDebitosFactory Register(string UF, Type repository) 30 | { 31 | if (!_Repositories.TryAdd(UF, repository)) 32 | { 33 | _Repositories[UF] = repository; 34 | } 35 | 36 | return this; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran/DetranVerificadorDebitosRepositoryCrawlerBase.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.Application.DTO; 2 | using DesignPatternSamples.Application.Repository; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace DesignPatternSamples.Infra.Repository.Detran 7 | { 8 | public abstract class DetranVerificadorDebitosRepositoryCrawlerBase : IDetranVerificadorDebitosRepository 9 | { 10 | public async Task> ConsultarDebitos(Veiculo veiculo) 11 | { 12 | var html = await RealizarAcesso(veiculo); 13 | return await PadronizarResultado(html); 14 | } 15 | 16 | protected abstract Task RealizarAcesso(Veiculo veiculo); 17 | protected abstract Task> PadronizarResultado(string html); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Infra.Repository.Detran/Infra.Repository.Detran.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | DesignPatternSamples.Infra.Repository.Detran 6 | DesignPatternSamples.Infra.Repository.Detran 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/WebAPI/Controllers/Detran/DebitosController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using DesignPatternSamples.Application.DTO; 3 | using DesignPatternSamples.Application.Services; 4 | using DesignPatternSamples.WebAPI.Models; 5 | using DesignPatternSamples.WebAPI.Models.Detran; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Mvc; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | 11 | namespace DesignPatternSamples.WebAPI.Controllers.Detran 12 | { 13 | [Route("api/Detran/[controller]")] 14 | [ApiController] 15 | public class DebitosController : ControllerBase 16 | { 17 | private readonly IMapper _Mapper; 18 | private readonly IDetranVerificadorDebitosService _DetranVerificadorDebitosServices; 19 | 20 | public DebitosController(IMapper mapper, IDetranVerificadorDebitosService detranVerificadorDebitosServices) 21 | { 22 | _Mapper = mapper; 23 | _DetranVerificadorDebitosServices = detranVerificadorDebitosServices; 24 | } 25 | 26 | [HttpGet()] 27 | [ProducesResponseType(typeof(SuccessResultModel>), StatusCodes.Status200OK)] 28 | public async Task Get([FromQuery]VeiculoModel model) 29 | { 30 | var debitos = await _DetranVerificadorDebitosServices.ConsultarDebitos(_Mapper.Map(model)); 31 | 32 | var result = new SuccessResultModel>(_Mapper.Map>(debitos)); 33 | 34 | return Ok(result); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/WebAPI/Mapper/DetranMapper.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using DesignPatternSamples.Application.DTO; 3 | using DesignPatternSamples.WebAPI.Models.Detran; 4 | 5 | namespace DesignPatternSamples.WebAPI.Mapper 6 | { 7 | public class DetranMapper : Profile 8 | { 9 | public DetranMapper() 10 | { 11 | CreateMap(); 12 | CreateMap(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WebAPI/Middlewares/ExceptionHandlingMiddleware.cs: -------------------------------------------------------------------------------- 1 | using DesignPatternSamples.WebAPI.Models; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | 9 | namespace DesignPatternSamples.WebAPI.Middlewares 10 | { 11 | public class ExceptionHandlingMiddleware : IMiddleware 12 | { 13 | private readonly ILogger _Logger; 14 | 15 | public ExceptionHandlingMiddleware(ILogger logger) 16 | { 17 | _Logger = logger; 18 | } 19 | 20 | public async Task InvokeAsync(HttpContext context, RequestDelegate next) 21 | { 22 | try 23 | { 24 | await next(context); 25 | } 26 | catch (Exception e) 27 | { 28 | _Logger.LogError(e, e.Message); 29 | await HandleExceptionAsync(context); 30 | } 31 | } 32 | 33 | private Task HandleExceptionAsync(HttpContext context) 34 | { 35 | var code = HttpStatusCode.InternalServerError; 36 | 37 | string result = JsonConvert.SerializeObject(new FailureResultModel("Ocorreu um erro inesperado")); 38 | 39 | context.Response.ContentType = "application/json"; 40 | context.Response.StatusCode = (int)code; 41 | 42 | return context.Response.WriteAsync(result); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/WebAPI/Models/AbstractResultModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DesignPatternSamples.WebAPI.Models 4 | { 5 | public abstract class AbstractResultModel : IResultModel 6 | { 7 | public TEntity Data { get; protected set; } 8 | 9 | public abstract bool HasSucceeded { get; } 10 | 11 | public IEnumerable Details { get; protected set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/WebAPI/Models/Detran/DebitoVeiculoModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DesignPatternSamples.WebAPI.Models.Detran 4 | { 5 | public class DebitoVeiculoModel 6 | { 7 | public DateTime DataOcorrencia { get; set; } 8 | public string Descricao { get; set; } 9 | public double Valor { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/WebAPI/Models/Detran/VeiculoModel.cs: -------------------------------------------------------------------------------- 1 | namespace DesignPatternSamples.WebAPI.Models.Detran 2 | { 3 | public class VeiculoModel 4 | { 5 | public string Placa { get; set; } 6 | public string UF { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/WebAPI/Models/FailureResultModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DesignPatternSamples.WebAPI.Models 4 | { 5 | public class FailureResultModel : AbstractResultModel 6 | { 7 | public override bool HasSucceeded => false; 8 | 9 | public FailureResultModel(TEntity data, IEnumerable details) 10 | { 11 | Data = data; 12 | Details = details; 13 | } 14 | } 15 | 16 | public class FailureResultModel : FailureResultModel 17 | { 18 | public FailureResultModel(IEnumerable details) : base(null, details) { } 19 | public FailureResultModel(ResultDetail detail) : this(new List() { detail }) { } 20 | public FailureResultModel(string detail) : this(new ResultDetail(detail)) { } 21 | } 22 | } -------------------------------------------------------------------------------- /src/WebAPI/Models/IResultModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DesignPatternSamples.WebAPI.Models 4 | { 5 | public interface IResultModel 6 | { 7 | bool HasSucceeded { get; } 8 | IEnumerable Details { get; } 9 | TEntity Data { get; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/WebAPI/Models/ResultDetail.cs: -------------------------------------------------------------------------------- 1 | namespace DesignPatternSamples.WebAPI.Models 2 | { 3 | public class ResultDetail 4 | { 5 | public string Message { get; } 6 | 7 | public ResultDetail(string message) 8 | { 9 | Message = message; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/WebAPI/Models/SuccessResultModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DesignPatternSamples.WebAPI.Models 4 | { 5 | public class SuccessResultModel : AbstractResultModel 6 | { 7 | public override bool HasSucceeded => true; 8 | 9 | public SuccessResultModel(TEntity data) 10 | { 11 | Data = data; 12 | } 13 | public SuccessResultModel(TEntity data, IEnumerable details) 14 | { 15 | Data = data; 16 | Details = details; 17 | } 18 | } 19 | 20 | public class SuccessResultModel : SuccessResultModel 21 | { 22 | public SuccessResultModel() : base(null, null) { } 23 | } 24 | } -------------------------------------------------------------------------------- /src/WebAPI/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | using Serilog; 4 | 5 | namespace DesignPatternSamples.WebAPI 6 | { 7 | public static class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | CreateHostBuilder(args).Build().Run(); 12 | } 13 | 14 | 15 | 16 | public static IHostBuilder CreateHostBuilder(string[] args) => 17 | Host.CreateDefaultBuilder(args) 18 | .ConfigureWebHostDefaults(webBuilder => 19 | { 20 | webBuilder.UseStartup(); 21 | }) 22 | .UseSerilog((builder, configuration) => 23 | { 24 | configuration.ReadFrom.Configuration(builder.Configuration); 25 | }); 26 | } 27 | } -------------------------------------------------------------------------------- /src/WebAPI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:58951", 8 | "sslPort": 44384 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "WebAPI": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/WebAPI/Startup.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fructuoso/DesignPatternSamples/7eafc0492b3e8dd89c4492f763a56a35887d8c01/src/WebAPI/Startup.cs -------------------------------------------------------------------------------- /src/WebAPI/WebAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | DesignPatternSamples.WebAPI 6 | DesignPatternSamples.WebAPI 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/WebAPI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Debug", 5 | "Override": { 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "WriteTo": [ 10 | { 11 | "Name": "Console" 12 | } 13 | ], 14 | "Enrich": [ "FromLogContext" ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Workbench.Comparer/GenericComparerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace Workbench.Comparer 6 | { 7 | public class GenericComparerFactory : IEqualityComparer 8 | { 9 | private Func Predicate { get; set; } 10 | 11 | private GenericComparerFactory() { } 12 | 13 | public static GenericComparerFactory Create(Func predicate) 14 | { 15 | return new GenericComparerFactory() { Predicate = predicate }; 16 | } 17 | 18 | public bool Equals([AllowNull] TEntity x, [AllowNull] TEntity y) 19 | { 20 | return Predicate(x).Equals(Predicate(y)); 21 | } 22 | 23 | public int GetHashCode([DisallowNull] TEntity obj) 24 | { 25 | return Predicate(obj).GetHashCode(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Workbench.Comparer/Workbench.Comparer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Workbench.DependencyInjection.Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.DependencyInjection.Extensions; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace Workbench.DependencyInjection.Extensions 7 | { 8 | public static class ServiceCollectionExtensions 9 | { 10 | public static IServiceCollection Decorate(this IServiceCollection services) 11 | where TInterface : class 12 | where TDecorator : class, TInterface 13 | { 14 | ServiceDescriptor innerDescriptor = services.FirstOrDefault(s => s.ServiceType == typeof(TInterface)); 15 | 16 | if (innerDescriptor == null) { throw new InvalidOperationException($"{typeof(TInterface).Name} is not registered"); } 17 | 18 | var objectFactory = ActivatorUtilities.CreateFactory( 19 | typeof(TDecorator), 20 | new[] { typeof(TInterface) }); 21 | 22 | services.Replace(ServiceDescriptor.Describe( 23 | typeof(TInterface), 24 | s => (TInterface)objectFactory(s, new[] { s.CreateInstance(innerDescriptor) }), innerDescriptor.Lifetime) 25 | ); 26 | 27 | return services; 28 | } 29 | 30 | private static object CreateInstance(this IServiceProvider services, ServiceDescriptor descriptor) 31 | { 32 | if (descriptor.ImplementationInstance != null) 33 | return descriptor.ImplementationInstance; 34 | 35 | if (descriptor.ImplementationFactory != null) 36 | return descriptor.ImplementationFactory(services); 37 | 38 | return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType); 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Workbench.DependencyInjection.Extensions/Workbench.DependencyInjection.Extensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Workbench.GenericComparer.Tests/GenericComparerFactoryTest.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fructuoso/DesignPatternSamples/7eafc0492b3e8dd89c4492f763a56a35887d8c01/src/Workbench.GenericComparer.Tests/GenericComparerFactoryTest.cs -------------------------------------------------------------------------------- /src/Workbench.GenericComparer.Tests/Workbench.GenericComparer.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | all 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Workbench.GenericComparer.Tests/coverage.json: -------------------------------------------------------------------------------- 1 | { 2 | "Workbench.Comparer.dll": { 3 | "C:\\Users\\victor.fructuoso\\source\\repos\\DesignPatternSamples\\src\\Workbench.Comparer\\GenericComparerFactory.cs": { 4 | "Workbench.Comparer.GenericComparerFactory`1": { 5 | "System.Func`2 Workbench.Comparer.GenericComparerFactory`1::get_Predicate()": { 6 | "Lines": { 7 | "9": 24 8 | }, 9 | "Branches": [] 10 | }, 11 | "Workbench.Comparer.GenericComparerFactory`1 Workbench.Comparer.GenericComparerFactory`1::Create(System.Func`2)": { 12 | "Lines": { 13 | "14": 2, 14 | "15": 2, 15 | "16": 2 16 | }, 17 | "Branches": [] 18 | }, 19 | "System.Boolean Workbench.Comparer.GenericComparerFactory`1::Equals(TEntity,TEntity)": { 20 | "Lines": { 21 | "19": 4, 22 | "20": 4, 23 | "21": 4 24 | }, 25 | "Branches": [] 26 | }, 27 | "System.Int32 Workbench.Comparer.GenericComparerFactory`1::GetHashCode(TEntity)": { 28 | "Lines": { 29 | "24": 14, 30 | "25": 14, 31 | "26": 14 32 | }, 33 | "Branches": [] 34 | }, 35 | "System.Void Workbench.Comparer.GenericComparerFactory`1::.ctor()": { 36 | "Lines": { 37 | "11": 6 38 | }, 39 | "Branches": [] 40 | } 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Workbench.IDistributedCache.Extensions/IDistributedCacheExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Caching.Distributed; 2 | using System; 3 | using System.Threading.Tasks; 4 | using Workbench.IFormatter.Extensions; 5 | using Abstractions = Microsoft.Extensions.Caching.Distributed; 6 | 7 | namespace Workbench.IDistributedCache.Extensions 8 | { 9 | public static class IDistributedCacheExtensions 10 | { 11 | public static TEntity GetOrCreate(this Abstractions.IDistributedCache cache, string key, Func predicate, int ttl) 12 | { 13 | TEntity entity = cache.Get(key); 14 | 15 | if (entity != null) { return entity; } 16 | 17 | entity = predicate(); 18 | 19 | cache.Set(key, entity, ttl); 20 | 21 | return entity; 22 | } 23 | public static async Task GetOrCreateAsync(this Abstractions.IDistributedCache cache, string key, Func> predicate, int ttl) 24 | { 25 | TEntity entity = await cache.GetAsync(key); 26 | 27 | if (entity != null) { return entity; } 28 | 29 | entity = await predicate(); 30 | 31 | await cache.SetAsync(key, entity, ttl); 32 | 33 | return entity; 34 | } 35 | 36 | public static TEntity Get(this Abstractions.IDistributedCache cache, string key) 37 | { 38 | TEntity result = default; 39 | 40 | var binary = cache.Get(key); 41 | 42 | if (binary != null) result = binary.Deserialize(); 43 | 44 | return result; 45 | } 46 | public static async Task GetAsync(this Abstractions.IDistributedCache cache, string key) 47 | { 48 | TEntity result = default; 49 | 50 | var binary = await cache.GetAsync(key); 51 | 52 | if (binary != null) result = binary.Deserialize(); 53 | 54 | return result; 55 | } 56 | 57 | public static void Set(this Abstractions.IDistributedCache cache, string key, TEntity entity, int ttl) 58 | { 59 | var options = new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(ttl)); 60 | var binary = entity.Serialize(); 61 | cache.Set(key, binary, options); 62 | } 63 | public static Task SetAsync(this Abstractions.IDistributedCache cache, string key, TEntity entity, int ttl) 64 | { 65 | var options = new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(ttl)); 66 | var binary = entity.Serialize(); 67 | return cache.SetAsync(key, binary, options); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Workbench.IDistributedCache.Extensions/Workbench.IDistributedCache.Extensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Workbench.IFormatter.Extensions.Tests/IFormaterExtensionsTests.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fructuoso/DesignPatternSamples/7eafc0492b3e8dd89c4492f763a56a35887d8c01/src/Workbench.IFormatter.Extensions.Tests/IFormaterExtensionsTests.cs -------------------------------------------------------------------------------- /src/Workbench.IFormatter.Extensions.Tests/Workbench.IFormatter.Extensions.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | all 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Workbench.IFormatter.Extensions/IFormatterExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Serialization = System.Runtime.Serialization; 3 | using System.Runtime.Serialization.Formatters.Binary; 4 | 5 | namespace Workbench.IFormatter.Extensions 6 | { 7 | 8 | public static class IFormatterExtensions 9 | { 10 | public static Serialization.IFormatter DefaultFormatter { get; set; } = new BinaryFormatter(); 11 | 12 | public static byte[] Serialize(this TEntity entity) => entity.Serialize(DefaultFormatter); 13 | public static byte[] Serialize(this TEntity entity, Serialization.IFormatter formatter) 14 | { 15 | if (entity == null) { return default; } 16 | 17 | using (MemoryStream stream = new MemoryStream()) 18 | { 19 | formatter.Serialize(stream, entity); 20 | return stream.ToArray(); 21 | } 22 | } 23 | 24 | public static TEntity Deserialize(this byte[] data) => data.Deserialize(DefaultFormatter); 25 | public static TEntity Deserialize(this byte[] data, Serialization.IFormatter formatter) 26 | { 27 | if (data == null) return default; 28 | 29 | using (MemoryStream stream = new MemoryStream(data)) 30 | { 31 | var obj = formatter.Deserialize(stream); 32 | return (TEntity)obj; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Workbench.IFormatter.Extensions/Workbench.IFormatter.Extensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Workbench.Linq.Extensions.Tests/DistinctExtensionsTests.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fructuoso/DesignPatternSamples/7eafc0492b3e8dd89c4492f763a56a35887d8c01/src/Workbench.Linq.Extensions.Tests/DistinctExtensionsTests.cs -------------------------------------------------------------------------------- /src/Workbench.Linq.Extensions.Tests/Workbench.Linq.Extensions.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | all 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Workbench.Linq.Extensions/DistinctExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Workbench.Comparer; 5 | 6 | namespace Workbench.Linq.Extensions 7 | { 8 | public static class DistinctExtensions 9 | { 10 | public static IEnumerable Distinct(this IEnumerable source, Func predicatee) 11 | { 12 | return source.Distinct(GenericComparerFactory.Create(predicatee)); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Workbench.Linq.Extensions/Workbench.Linq.Extensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test-coverage.bat: -------------------------------------------------------------------------------- 1 | rmdir CoverageResults /s /q 2 | 3 | dotnet test ./src/DesignPatternSamples.sln ^ 4 | /p:CollectCoverage=true ^ 5 | /p:CoverletOutput=../../CoverageResults/ ^ 6 | /p:MergeWith="../../CoverageResults/coverage.json" ^ 7 | /p:CoverletOutputFormat=\"cobertura,json,opencover\" ^ 8 | -m:1 9 | 10 | reportgenerator ^ 11 | -reports:"CoverageResults/coverage.cobertura.xml" ^ 12 | -targetdir:"CoverageResults/Report" ^ 13 | -reporttypes:Html;HTMLSummary 14 | 15 | start "" "./CoverageResults/Report/index.html" 16 | 17 | pause --------------------------------------------------------------------------------