Asp.Net Core dependency injection utilities. This project aims at adding a few missing pieces to the Asp.Net Core container, without the need to replace it by a third-party.
What is supported:
- Contextual dependency injection
- Module-based dependency registration with auto-discovery
What I'd like to add:
- Automatic factory creation, based on interfaces
The following packages are bundling multiple packages as convenience.
Bundles containing the following packages:
ForEvolve.DependencyInjection.ContextualBindings
ForEvolve.DependencyInjection.Modules
dotnet add package ForEvolve.DependencyInjection
Bundles containing the following packages:
ForEvolve.DependencyInjection
ForEvolve.DependencyInjection.ContextualBindings.AspNetCore
dotnet add package ForEvolve.DependencyInjection.AspNetCore
The goal of the ForEvolve.DependencyInjection.ContextualBindings
library is to add support for contextual dependency injection to any application.
What is currently working:
- Transient contextual injection
- Scoped contextual injection
- Singleton contextual injection
- Contextual injection in MVC controllers
- Contextual object trees building
dotnet add package ForEvolve.DependencyInjection.ContextualBindings
# OR
dotnet add package ForEvolve.DependencyInjection.ContextualBindings.AspNetCore
You can create simple contextual bindings like:
// Inject a LastNameGenerator instance into the INameGenerator parameter of FirstNameService
services
.AddContextualBinding<IFirstNameService, FirstNameService>(d => d
.WithConstructorArgument<INameGenerator, FirstNameGenerator>())
;
// Inject a LastNameGenerator instance into the INameGenerator parameter of LastNameService
services
.AddContextualBinding<ILastNameService, LastNameService>(d => d
.WithConstructorArgument<INameGenerator, LastNameGenerator>())
;
// Inject a FirstNameGenerator in the first INameGenerator parameter and
// inject a LastNameGenerator in the second INameGenerator parameter of FullNameGenerator.
services
.AddContextualBinding<INameGenerator, FullNameGenerator>(d => d
.WithConstructorArgument<INameGenerator, FirstNameGenerator>()
.WithConstructorArgument<INameGenerator, LastNameGenerator>())
;
Or go for more complex object trees like this:
services
.AddContextualBinding<IComplexObjectTreeService, ComplexObjectTreeService>(d =>
{
d.WithConstructorArgument<IDirectDependency, DirectDependency1>(d => d
.WithConstructorArgument<ISubDependency1, SubDependency1_1>()
.WithConstructorArgument<ISubDependency2, SubDependency2_1>()
.WithConstructorArgument<ISubDependency3, SubDependency3_1>()
);
d.WithConstructorArgument<IDirectDependency, DirectDependency2>(d => d
.WithConstructorArgument<ISubDependency1, SubDependency1_2>()
.WithConstructorArgument<ISubDependency2, SubDependency2_2>()
.WithConstructorArgument<ISubDependency3, SubDependency3_2>()
);
})
;
The ForEvolve.DependencyInjection.ContextualBindings.AspNetCore
library adds support for contextual dependency injection into controller's constructors. It extend the capabilities of ForEvolve.DependencyInjection.ContextualBindings
. Under the hood, it decorates the default IControllerActivator
by a custom implementation, loading conditional bindings when they exist and falling back to the default implementation when none does.
This project uses
Scrutor
to decorate theIControllerActivator
.
dotnet add package ForEvolve.DependencyInjection.ContextualBindings.AspNetCore
To enable controller injection, you must register the IControllerActivator
decorator by calling the WithContextualBindings()
extension method on an IMvcBuilder
, like this:
services
.AddControllers()
.WithContextualBindings();
Then you can use the capabilities of ForEvolve.DependencyInjection.ContextualBindings
, but for controllers, like this:
services
.AddContextualBinding<FirstController>(d => d
.WithConstructorArgument<IService, Implementation1>())
.AddContextualBinding<SecondController>(d => d
.WithConstructorArgument<IService, Implementation2>())
;
The goal of the ForEvolve.DependencyInjection.Modules
library is to allow splitting DI bindings into modules and enabling auto-discovery of those modules.
dotnet add package ForEvolve.DependencyInjection.Modules
To enable and scan for modules, use the following code:
services.AddDependencyInjectionModules(typeof(Program).Assembly);
// OR
services.AddDependencyInjectionModules(typeof(AnyClassThatIsPartOfTheAssemblyThatYouWantToScan).Assembly, typeof(ClassFromAnotherAssembly).Assembly);
To create a module you can implement IDependencyInjectionModule
or inherit from DependencyInjectionModule
.
You can inject any dependencies that you want in your constructor, as long as you defined them (see bellow).
Once you have a class, register your dependencies, like this:
public class SomeImportantDIModule : DependencyInjectionModule
{
public ContextualServiceInjectionModule(IServiceCollection services)
: base(services)
{
services.AddSingleton<ISomeService, SomeImplementation>();
services
.AddContextualBinding<FirstController>(d => d
.WithConstructorArgument<IService, Implementation1>())
;
// ...
}
}
You can also register custom dependencies that can be injected in your modules like this:
services
.AddDependencyInjectionModules(initialize: false)
.ScanAssemblies(typeof(Program).Assembly)
.UseConfiguration(configuration)
.ConfigureServices(services => services.TryAddSingleton<ISomeInterface, SomeImplementation>())
.Initialize()
;
// ...
public class SomeOtherModule : DependencyInjectionModule
{
public ContextualServiceInjectionModule(IServiceCollection services, ISomeInterface someInterface)
: base(services)
{
// You can now use `someInterface` to do something...
}
}
The dependencies are only used during the registration process, are only added to a private IServiceCollection
, and are not added to the application IServiceCollection
. Use these only to initialize modules.
As long as the assembly containing the modules are scanned, you can split your bindings as you want and you don't need to do anything else.
The work on this project is not yet started
The goal of this library is to generate factories automatically, based on an interface. For example, the idea would be to implement the following interface automatically.
public interface IFactory
{
IService CreateService();
}
The initial implementation would be a simple service locator, that gets IService
from the container. The design may change along the way to support more complex scenarios.
Please open an issue and be as clear as possible; see How to contribute? for more information.
If you would like to contribute to the project, first, thank you for your interest, and please read Contributing to ForEvolve open source projects for more information.
Also, please read the Contributor Covenant Code of Conduct that applies to all ForEvolve repositories.