.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
IHostis a recent abstraction.- The classic .NET
WebHostwas made generic allowing for other hosts such asOrleansHost. - In fact one can now think of
IHostas 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
IServiceCollectionallows you to register a class against a given interface.IServiceProviderprovides you interfaces fromIServiceCollection- Dependency injection originates from
IHostBuilderproviding a rich API for customizing yourIHost.- Logging, SignalR, cache, DB to name a few.
- The
IHostBuildermaintains a private instance ofIServiceCollectionwhich is injected into theIHost. IHostis responsible for injecting interfaces when requested by a class.- Examples of this are classes inheriting
Controllerin web APIs andGrainin 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.