1. Cenário:
Pra entender o objetivo do post, preciso te apresentar meu cenário:
- Esse código foi usado em um software que implementa a arquitetura em fatias slice architecture
- Quando eu busco uma informação no meu banco de dados, e vou retornar pra "View", preciso transformar os dados, para um "DTO".
- Ao invés de usar AutoMapper nessa transformação, implementei o design pattern Adapter manualmente, seguindo esse artigo.
Implementando o design pattern Adapter:
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 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.
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.
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));
}
}
// 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.
Top comments (1)
Muito bom.
Reflection como sempre fazendo a mágica para simplificar nossa vida rsrs'.
Me fez lembrar que já fiz algo parecido, só que era para mapear os Profiles do AutoMapper e outra situação parecida que não estou me lembrando agora.