In this article, I will show you how to implement Open Generic Constructor Injection
in .NET core to make the software Extensible and Maintainable. Let’s assume you want to implement an Audit system in your application, and currently, you are logging Audit info to the console.
To understand the problem, I have created a simple application in .net core that implements an audit log system without open generic.
Below is the complete source code.
Main.cs
void Main()
{
var container = Startup.Configure();
var customerService = container.GetService<ICustomerService>();
var fooService = container.GetService<FooService>();
fooService.DoSomething();
customerService.Get(1);
}
Startup.cs
public class Startup
{
public static ServiceProvider Configure()
{
var provider = new ServiceCollection()
.AddSingleton<ICustomerService, CustomerService>()
.AddScoped<IAuditLog<CustomerService>, ConsoleAuditLog<CustomerService>>()
.AddLogging(fs => fs.AddConsole())
.BuildServiceProvider(validateScopes: false);
return provider;
}
}
Customer.cs
public class Customer
{
public int Id { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
ICustomerService.cs
public interface ICustomerService
{
Customer Get(int id);
}
CustomerService
public class CustomerService : ICustomerService
{
private readonly IAuditLog<CustomerService> _auditManager;
public CustomerService(IAuditLog<CustomerService> auditManager)
{
_auditManager = auditManager;
}
public Customer Get(int id)
{
// TODO: simulate getting customer from the database.
var customer = new Customer() { Id = 1, Email = "john@doe.com", Age = 21 };
_auditManager.Log("GET");
return customer;
}
}
public interface IAuditLog<T>
{
void Log(string action);
}
public class ConsoleAuditLog<T> : IAuditLog<T>
{
public string SourceName { get; }
public ConsoleAuditLog()
{
SourceName = typeof(T).Name;
}
public void Log(string action)
{
Console.WriteLine($"{SourceName}- Logging {action} to Console");
}
}
If you run the application, you will get the following output
CustomerService- Logging GET to Console
What is the problem with the current implementations?
After some time, you added more service to your project, then you have to register all the dependency into the DI container. For example, I am adding FooService
and BarService
.
public static ServiceProvider Configure()
{
var provider = new ServiceCollection()
.AddSingleton<ICustomerService, CustomerService>()
.AddSingleton<FooService>()
+ .AddScoped<IAuditLog<CustomerService>, ConsoleAuditLog<CustomerService>>()
+ .AddScoped<IAuditLog<FooService>, ConsoleAuditLog<FooService>>()
+ .AddScoped<IAuditLog<BarService>, ConsoleAuditLog<FooService>>()
.AddLogging(fs => fs.AddConsole())
.BuildServiceProvider(validateScopes: false);
return provider;
}
We can improve this solution by using Open Generic Constructor Injection
which .net core provides out of the box. In part II, I will show you how to implement Open Generic
to avoid registering the services in a container.
Open Generics in ASPNET Core Dependency Injection With Real World Example-Part2