-
Notifications
You must be signed in to change notification settings - Fork 10
2.1 Dependency injection
Dependency injection (DI) is one of the design pattern which apply the Inversion of Control (IoC). Inversion of Control is a design principle which is a high level guidelines and don’t care about the implementation.
Dependency Injection solves problems such as:
- How can an application or class be independent of how its objects are created?
- How can the way objects are created be specified in separate configuration files?
- How can an application support different configurations?
Creating objects directly within the class that requires the objects is inflexible because it commits the class to particular objects and makes it impossible to change the instantiation later independently from (without having to change) the class. It stops the class from being reusable if other objects are required, and it makes the class hard to test because real objects can not be replaced with mock objects.
A class is no longer responsible for creating the objects it requires, and it does not have to delegate instantiation to a factory object as in the Abstract Factory design pattern.
Lets assume we have a service interface IMyService
that we need to use in MyProcess
// Definition of some service that we need injecting
public interface IMyService
{
void Bar();
}
We are creating a concrete implementation of that service interface and decorate that implementation with an [Export]
attribute that tells the DI container what injectable type we are implementing.
// Export this class MyService as injectable type IMyService
[Export(typeof(IMyService))]
public class MyService : IMyService
{
public void Bar()
{
// Do something
}
}
Finaly when we want to use the dependency, we inject it with the attribute [Import]
public class MyProcess
{
// The attribute import instructs the DI container to inject an instance of type IMyService
// The [Export] attribute of 'MyService' class tells the DI container to inject insatace of that class here
[Import] private IMyService Service { get; set; }
public void Foo1()
{
// The dependency is already injected
Service.Bar();
}
public void Foo2()
{
// We get the instace through the Signals.Aspects.DI.SystemBootstrapper that contains the DI container
var service = SystemBootstrapper.GetInstance<IMyService>();
service.Bar();
}
}
All [Export]
types are registered as transient.
All Signals aspects are registered internally as transient.
Signals offers multiple implementations for the Dependency Injection aspect out of the box:
- Signals.Aspects.DI.Autofac
- Signals.Aspects.DI.SimpleInjector
- Signals.Aspects.DI.DotNetCore
ApplicationBootstrapConfiguration
accepts instance of RegistrationService
of any implementation package with no properties.
###ASP.NET Core integration###
Autofac
- Install required packages
- Install package Signals.Aspects.DI.Autofac
- Install package Autofac.Extensions.DependencyInjection
- Add any dependencies that are needed (this includes
IServiceCollection
extensions like.AddDataProtection()
)
public static IServiceProvider AddSignals(this IServiceCollection services)
{
services.AddTransient<>();
// ...add any dependency into .NET Core dependency container
// Step 3 continues...
- Populate Autofac container with the dependencies registered in the .NET Core container
var registrationService = new RegistrationService();
registrationService.Builder.Populate(services);
// Step 4 continues...
- Add any additional dependencies that are needed
registrationService.Register<>();
// ...add any dependency into Signals dependency container
// Step 5 continues...
- Register the deplendency container into Signals
services
.AddSignals(config =>
{
config.RegistrationService = registrationService;
});
// Step 6 continues...
- Let ASP.NET Core use our Autofac integration as DI container
return new AutofacServiceProvider(registrationService.ServiceContainer.Container);
}
Simple injector
- Install required packages
- Install package Signals.Aspects.DI.SimpleInjector
- Add any dependencies that are needed (this includes
IServiceCollection
extensions like.AddDataProtection()
)
public static IServiceProvider AddSignals(this IServiceCollection services)
{
services.AddTransient<>();
// ...add any dependency into .NET Core dependency container
var registrationService = new RegistrationService();
registrationService.Register<>();
// ...add any dependency into Signals dependency container
// Step 3 continues...
- Register the deplendency container into Signals
services
.AddSignals(config =>
{
config.RegistrationService = registrationService;
});
// Step 4 continues...
- Populate .NET Core container with the dependencies registered in the simple injector container
foreach (var service in registrationService.Builder.GetCurrentRegistrations())
services.Add(new ServiceDescriptor(service.ServiceType,
(provider) => registrationService.Builder.GetInstance(service.ServiceType),
ServiceLifetime.Transient));
// Step 5 continues...
- Let ASP.NET Core use its own integration as DI container
return services.BuildServiceProvider();
}
DotNetCore
- Install required packages
- Install package Signals.Aspects.DI.DotNetCore
- Add any dependencies that are needed (this includes
IServiceCollection
extensions like.AddDataProtection()
)
public static IServiceProvider AddSignals(this IServiceCollection services)
{
services.AddTransient<>();
// ...add any dependency into .NET Core dependency container
var registrationService = new RegistrationService();
registrationService.Register<>();
// ...add any dependency into Signals dependency container
// Step 3 continues...
- Register the deplendency container into Signals
services
.AddSignals(config =>
{
config.RegistrationService = registrationService;
});
// Step 4 continues...
- Let ASP.NET Core use its own integration as DI container
return registrationService.ServiceContainer.Container;
}
###Background application###
- Install either of the required packages
- Install package Signals.Aspects.DI.Autofac
- Install package Signals.Aspects.DI.SimpleInjector
- Add any dependencies that are needed
public class Program
{
public static void Main(string[] args)
{
var registrationService = RegistrationService();
registrationService.Register<>();
// ...add any dependency into Signals dependency container
// Step 3 continues...
- Register the deplendency container into Signals
public class Program
{
public static void Main(string[] args)
{
var config = new BackgroundApplicationBootstrapConfiguration
{
RegistrationService = registrationService
}
// Step 4 continues...
- Bootstrap the application
var assemblies = Directory
.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "App.*.dll")
.Select(file => Assembly.LoadFrom(file))
.ToArray();
config.Bootstrap(assemblies);
}
}
- Install package Signals.Aspects.DI
- Create class with implementation of
Signals.Aspects.DI.IRegistrationService
Registration methods are meant to be used for registering transient dependencies.
The registration service is beeing used for bootstrapping Signals aspects thruought the entire application, as well as your custom types that have the [Export]
attribute (like repositories).
/// <summary>
/// Dependency resolver builder
/// </summary>
public class MyRegistrationService : IRegistrationService
{
/// <summary>
/// Create service container by building registration service
/// </summary>
/// <returns></returns>
IServiceContainer Build();
/// <summary>
/// Register interface with implementation
/// </summary>
/// <typeparam name="TDefinition"></typeparam>
/// <typeparam name="TImplementation"></typeparam>
void Register<TDefinition, TImplementation>()
where TDefinition : class
where TImplementation : class, TDefinition;
/// <summary>
/// Register interface with implementation
/// </summary>
/// <param name="serviceType"></param>
/// <param name="implementationType"></param>
void Register(Type serviceType, Type implementationType);
/// <summary>
/// Register type without interface
/// </summary>
/// <typeparam name="TImplementation"></typeparam>
void Register<TImplementation>()
where TImplementation : class;
/// <summary>
/// Register type without interface
/// </summary>
/// <param name="implementationType"></param>
void Register(Type implementationType);
/// <summary>
/// Register interface with implementation instance
/// </summary>
/// <param name="serviceType"></param>
/// <param name="instance"></param>
void Register(Type serviceType, object instance);
/// <summary>
/// Register interface with implementation instance
/// </summary>
/// <typeparam name="TDefinition"></typeparam>
/// <param name="instance"></param>
void Register<TDefinition>(TDefinition instance)
where TDefinition : class;
/// <summary>
/// Register interface with implementation callback
/// </summary>
/// <param name="serviceType"></param>
/// <param name="callback"></param>
void Register(Type serviceType, Func<object> callback);
/// <summary>
/// Register interface with implementation callback
/// </summary>
/// <typeparam name="TDefinition"></typeparam>
/// <param name="callback"></param>
void Register<TDefinition>(Func<TDefinition> callback)
where TDefinition : class;
}
- Create class with implementation of Signals.Aspects.DI.IServiceContainer
/// <summary>
/// Dependency resolver
/// </summary>
public class MyServiceContainer : IServiceContainer
{
/// <summary>
/// Get instance of type
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <returns></returns>
TService GetInstance<TService>() where TService : class;
/// <summary>
/// Get instance of type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
object GetInstance(Type serviceType);
}
- Create wrappers for existing dependency resolvers if needed
4.1 Create wrapper for
Microsoft.Extensions.DependencyInjection.IServiceProvider
for ASP.NET Core
public class MyServiceProviderWrapper : IServiceProvider
{
/// <summary>
/// DI container
/// </summary>
private IServiceContainer serviceContainer;
/// <summary>
/// CTOR
/// </summary>
/// <param name="serviceContainer"></param>
public MyDependencyResolver(IServiceContainer serviceContainer)
{
this.serviceContainer = serviceContainer;
}
/// <summary>
/// Gets the service object of the specified type.
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return serviceContainer.GetInstance(serviceType);
}
}
- Use our implementation of IRegistrationService when configuring our application 5.1 ASP.NET Core registration example
public static IServiceProvider AddSignals(this IServiceCollection services)
{
var registrationService = new MyRegistrationService();
registrationService.Register<>();
// ...add any dependency into Signals dependency container
services
.AddSignals(config =>
{
config.RegistrationService = registrationService;
});
return new MyServiceProviderWrapper(registrationService.ServiceContainer);
}