Summary
.NET Core comes with a built-in dependency injection in the form of IServiceCollection which serves as the registry and IServiceProvider which serves as the service locator. You typically register a service by adding it to the IServiceCollection. A very minimal example could look like this:
NOTE: Using the Nuget package Microsoft.Extensions.DependencyInjection
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public static IServiceProvider Initialise()
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
return serviceCollection.BuildServiceProvider();
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IService, ServiceImplementation>();
}
}
1. Scenario: Register a service with constructor parameters known at the time of registration
services.AddTransient<IMyService>(s => new MyService("MyConnectionString"));
2. Scenario: Register an open generic
services.AddScoped(
typeof(IGenericRepository<>),
typeof(EFGenericRepository<>));
// Alternative Approach
services.Add(
new ServiceDescriptor(
typeof(IPipelineBehavior<,>),
typeof(LoggingBehavior<,>), ServiceLifetime.Scoped));
3. Scenario: ValidateScopes as a parameter when building ServiceProvider
Detailed explanation on Stackoverflow.
This option will prevent resolving of scoped services from the singleton container, that is if you accidentally try to resolve a scoped service within Configure method, you will get an exception. Whereas if you disable it you shouldn't.
4. Scenario: Use decorator pattern with Scrutor library
Scrutor follows a convention based approach to wire up an interface to implementation. Andrew Lock's Blog
5. Scenario: Multiple implementations of an interface, Use named resolution
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<Consumer>();
services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();
services.AddTransient<Func<string, IService>>(serviceProvider => key =>
{
switch(key)
{
case "A":
return serviceProvider.GetService<ServiceA>();
case "B":
return serviceProvider.GetService<ServiceB>();
case "C":
return serviceProvider.GetService<ServiceC>();
default:
throw new KeyNotFoundException(); // or maybe return null, up to you
}
});
}
}
public class Consumer
{
private readonly Func<string, IService> serviceAccessor;
public Consumer(Func<string, IService> serviceAccessor)
{
this.serviceAccessor = serviceAccesor;
}
public void UseServiceA()
{
//use serviceAccessor field to resolve desired type
serviceAccessor("A").DoIServiceOperation();
}
}