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.