Injeção de Dependência: escaneando e registrando
Tags: csharp, aspnetcore, dependencyinjection
Published: 2020-07-15 20:55:58

## 1. Cenário:

Pra entender o objetivo do post, preciso te apresentar meu cenário:
1. Esse código foi usado em um software que implementa a arquitetura em fatias [slice architecture](https://jimmybogard.com/vertical-slice-architecture/)
2. Quando eu busco uma informação no meu banco de dados, e vou retornar pra "View", preciso transformar os dados, para um "DTO".
3. Ao invés de usar AutoMapper nessa transformação, implementei o design pattern Adapter manualmente, seguindo esse [artigo](https://gigi.nullneuron.net/gigilabs/the-adapter-design-pattern-for-dtos-in-c/).

### Implementando o design pattern [Adapter](https://www.devmedia.com.br/um-pattern-em-csharp-por-dia-adapter-pattern-estrutural/21751):

```csharp
public class ProductAdapter : IScopedService
    {
        private readonly IAsyncDocumentSession _dbSession;

        public ProductAdapter(
            IAsyncDocumentSession dbSession)
        {
            _dbSession = dbSession;
        }

// Entra um objeto Command, sai um product
        public Product AdaptToProduct(ProductCreateEdit.Command source)
        {
            var destination = new Product();
            destination.Id = source.Id;
            destination.Description = source.Description;
            

            return destination;
        }
        
// Entra um product, sai um Command
        public ProductCreateEdit.Command AdaptToCommand(Product source)
        {
            var destination = new ProductCreateEdit.Command();
            destination.Id = source.Id;
            destination.Description = source.Description;

            return destination;
        }
    }
```
Não se apegue aos nomes das classes, entenda somente que são 2 métodos, que recebem um objeto, copiam as propriedades, e retornam outro.

## 2. Problema:

Como estou usando ASP.NET CORE 3.0 no desenvolvimento, preferi usar o sistema de [injeção de dependências](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1) nativo.  

Ao longo do projeto, vão ser criadas muitas classes "Adaptadoras", e registrá-las manualmente, uma a uma, é um trabalho que quero evitar.


## 3. Solução:

3.1 Criei uma interface de marcação. Uma interface que tem o único propósito de deixar uma marca nas classes adaptadoras.

```csharp
public interface IInterface
    {
        // helper interface
    }

public interface IScopedService : IInterface
    {
        // empty interface to ease injection
    }
```

Toda classe adaptadora que eu crio, a faço implementar a interface IScopedService.
Daí usando o método de extensão abaixo, eu escaneio todas as classes que implementem IScopedService, e as registro, com o ciclo de vida *Scoped*, no container de injeção de dependências.

```csharp
public static class ServiceCollectionExtensions
    {
        /// <summary>
        /// Register all types that implements T interface
        /// Reference:
        /// https://medium.com/agilix/asp-net-core-inject-all-instances-of-a-service-interface-64b37b43fdc8
        /// </summary>
        /// <param name="services">Service collection</param>
        /// <param name="assemblies">Assemblies that will be scanned</param>
        /// <param name="lifetime">lifetime scope</param>
        /// <typeparam name="T">Interface type</typeparam>
        public static void RegisterAllTypesThatImplements<I>(this IServiceCollection services, Assembly[] assemblies,
            ServiceLifetime lifetime = ServiceLifetime.Transient) where I : IInterface
        {
            var typesFromAssemblies = assemblies.SelectMany(a => a.DefinedTypes.Where(x => x.GetInterfaces().Contains(typeof(I))));
            foreach (var type in typesFromAssemblies)
                services.Add(new ServiceDescriptor(type, type, lifetime));
        }
    }
```

```csharp
// Agora na classe Startup.cs
public void ConfigureServices(IServiceCollection services)
{
   // registra todos os scoped services
   services.RegisterAllTypesThatImplements<IScopedService>();
}
```

## Conclusão:

Esse tipo de implementação não costuma aparecer nos cursos de programação.
Se você ignorar o fato de que estou usando esse registro em massa pra uma classe de mapeamento, verá que dá pra usar essa técnica pra registro em massa de classes que não implementam interfaces!

Se você encontrou esse post no momento certo, ele vai te economizar um tempo em manutenção / digitação de código.