Join Jess Chadwick for an in-depth discussion in this video Increasing maintainability with dependency injection, part of ASP.NET Core: Razor Pages.
- [Voiceover] Now that I've shown you how to use layouts to help separate the content of individual pages from the overall site layout and the page model approach to keep logic out of your views, it's time to introduce perhaps the most effective way to encourage separation of concerns, and that is with a pattern called dependency injection. Put simply, dependency injection means that a component's dependencies are provided to or injected into it so the component doesn't have to worry about choosing a specific implementation or know how to create or configure an instance of that dependency.
To really understand that power of dependency injection, and how it can help simplify your code and encourage loose coupling, I'll apply it to some of the code I have already written to see what a difference it can make. So far, I've already taken the first step toward loose coupling in my application by creating the RecipeService class to hold all of the logic around retrieving or editing recipes. However, my pages have also been responsible for creating new instances of the RecipeService and though it may seem innocent enough for pages to create this class, it's certainly not ideal.
By creating its own instance of the RecipeService, this page is now tied to that specific implementation as opposed to any implementation of the IRecipeService interface that it implements. And based on the methods that my pages are calling, they really shouldn't care which implementation of this interface they get. The other problem with this approach is that the page also needs to know how to create a new instance of the RecipeService, and quite frankly, that's not its job.
To address both of these problems, I'll refactor this code to use ASP.net cores excellent support for dependency injection to create and inject this service for me. There are two steps for applying dependency injection. First, you configure ASP.net core by telling it what dependency should be created and how to create them and then you actually request an instance of the dependency from within the page that needs it.
To configure dependencies, you'll need to head over to the start-up class. Then, go to the configure services method. Here I can tell ASP.net core that it should provide an instance of the RecipeService class any time an instance of the interface IRecipeService is requested. (keys typing) The method AddTransient is actually one of three different methods that configure dependencies in slightly different ways.
This one, AddTransient, tells ASP.net core to create a new instance of the class every single time one is requested. The AddSingleton method tells ASP.net core to create one single instance of the class for the entire lifetime of the application and return that same instance every time one is requested. And the third method, AddScoped, is kind of like AddSingleton, except that it tells ASP.net core to create one single instance of the class for each web request, reuse that instance for the entire request, and then clean it up when the request is done.
With this line in place, the dependency injection framework now knows how to create an instance of an IRecipeService. So now let's switch back to the view and request it. I'll start by removing line three where I create my own instance and I'll replace it with a call to the razor directive at inject. The inject directive is kind of like defining a property on your page that ASP.net core automatically populates for you and to use it, you provide it with the type of the class you'd like injected followed by the name of the property you'd like to create.
With this line in place, I can reference this property at runtime just the same as if I'd instantiated it myself. And now I can run the page to show that everything still works. Likewise, I can make the same changes to the recipe page. (keys typing) Even better still, since I'm using this directive in multiple places, I can move it into my view imports and automatically create the RecipeService property in every single page of the site.
Take a look - the site has been cleaned up a bit, and it still works great.
- Creating a new application
- Setting up pages
- Rendering dynamic content
- Reusing markup with layouts
- Increasing the maintainability of pages
- Processing data
- Validating input
- Securing an application