public class MyDependency
{
public void WriteMessage(string message)
{
Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
}
}
A class can create an instance of the MyDependency
class to make use of its WriteMessage
method. In the following example, the MyDependency
class is a dependency of the IndexModel
class:
public class IndexModel : PageModel
{
private readonly MyDependency _dependency = new MyDependency();
public void OnGet()
{
_dependency.WriteMessage("IndexModel.OnGet created this message.");
}
}
The class creates and directly depends on the MyDependency
class.
Code dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:
- To replace
MyDependency
with a different implementation, the IndexModel
class must be modified.
- If
MyDependency
has dependencies, they must also be configured by the IndexModel
class. In a large project with multiple classes depending on MyDependency
, the configuration code becomes scattered across the app.
- This implementation is difficult to unit test. The app should use a mock or stub
MyDependency
class, which isn't possible with this approach.
Loose coupled classes
Dependency injection addresses these problems through:
- The use of an interface or base class to abstract the dependency implementation.
- Registration of the dependency in a service container. ASP.NET Core provides a built-in service container, IServiceProvider. Services are typically registered in the app's
Startup.ConfigureServices
method.
- Injection of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.
In the sample app, the IMyDependency
interface defines the WriteMessage
method:
public interface IMyDependency
{
void WriteMessage(string message);
}
This interface is implemented by a concrete type, MyDependency
:
The sample app registers the IMyDependency
service with the concrete type MyDependency
. The AddScoped method registers the service with a scoped lifetime:
In the sample app, the IMyDependency
service is requested and used to call the WriteMessage
method, it is injucted into the client class, Index2Model, through the constructor:
By using the DI pattern, the controller:
- Doesn't use the concrete type
MyDependency
, only the IMyDependency
interface it implements. That makes it easy to change the implementation that the controller uses without modifying the controller.
- Doesn't create an instance of
MyDependency
, it's created by the DI container.