.NET CORE DI
Integrating with .NET CORE dependency injection patterns.
.NET Core DI
Assumptions
Before we dive into the features of the .NET dependency injection it’s important to understand what dependency injection is.
- Dependency injection is a pattern where classes can request their dependencies.
- In .NET Core
IHost
is a recent abstraction.- The classic .NET
WebHost
was made generic allowing for other hosts such asOrleansHost
. - In fact one can now think of
IHost
as the center of most modern .NET Core applications. - You might want to create your own builder/host in order to abstract configurations from external developers.
- The classic .NET
Key principals
IServiceCollection
allows you to register a class against a given interface.IServiceProvider
provides you interfaces fromIServiceCollection
- Dependency injection originates from
IHostBuilder
providing a rich API for customizing yourIHost
.- Logging, SignalR, cache, DB to name a few.
- The
IHostBuilder
maintains a private instance ofIServiceCollection
which is injected into theIHost
. IHost
is responsible for injecting interfaces when requested by a class.- Examples of this are classes inheriting
Controller
in web APIs andGrain
in Orleans clusters.
- Examples of this are classes inheriting
Although it’s worth noting that requesting interfaces directly from IServiceProvider
is considered an anti-pattern, one reason is that constructor injection of interfaces is considered standard. More on that can be found here.
Lifetime
The .NET dependency injection provides allows you to register your classes with one of the following lifetimes:
- Scoped
- When your class is registered as a scoped class the same instance will be used for the entire request, afterwards it’s disposed of.
- This is suitable when your class maintains a state that should be scoped to the request.
- Singleton
- When your class is registered as a singleton class the same instance will be used for all requests.
- This is suitable when your class maintains a state that should be considered global.
- Transient
- When your class is registered as a transient class a new instance will always be used
- This is suitable when your class maintains no state.
Register Class
When you are registering a class, you are registering an implementation that should occur each time a contract is invoked.
Example:
A common scenario is the ConfigureServices
function provided by the web framework in .NET.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyClass, MyClass>();
}
Here we are registering MyClass
against IMyClass
, meaning each time IMyClass
is invoked we’re using the implementation as defined in MyClass
Example:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyClass, MyClass>();
services.AddTransient<IMyClass, MyClass2>();
}
Here we are registering multiple classes against an interface.
Request Class
In .NET the standard pattern for requesting classes is through constructor injection.
Example:
public AClass(IMyClass myClass)
{
this.myClass = myClass;
}
Here our class AClass
has requested its dependency on IMyClass
and assigned it to a local variable.
Note: When requesting a single dependency you will get the latest registered class, meaning MyClass2
if both MyClass
and MyClass2
are both registered against IMyClass
.
Example:
public AClass(IEnumerable<IMyClass> myClass)
{
this.myClasses = myClasses;
}
Here our class AClass
has requested its dependency on all implementations of IMyClass
and assigned to a local variable.