Structuring Dependency Injection In ASP.NET Core The Right Way

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
how big is the file containing all of the service configurations for dependency injection in your application if it's growing more with every new service that you introduce you're going to have trouble maintaining that over time I'm going to show you two ways to solve that problem and then you can decide which one you want to use in your own application I'm inside of program.cs over.net 7 web API and you can see I have a lot of code for configuring Services inside of it we are configuring redis then adding some of our own Services we are adding mediator the database context some background jobs we're also adding authentication and authorization services and you can see we have close to a hundred lines of code for configuring our services if we continue to use this approach our program.cs file is going to grow out of control and then maintainability is going to become a real concern for our application I'm going to show you one way to solve this using extension methods on the iservice collection interface and this is probably approach that you're already familiar with because this is what is used inside of the framework by Microsoft I'm going to add a new folder where I'm going to place all of these service configurations I'm going to name this folder configuration and let's add a static class inside that's going to hold our extension methods let's call our class dependency injection and add it to our project and let's define an extension method that's going to hold our service configuration code the method is going to be public and static it's going to return an i service collection instance this is so that I can chain multiple calls to configure services using the Builder syntax and let's call this first method add caching because I'm going to configure services for caching inside of this method so all that we have to do now is go back to program.cs and pull out the logic that is configuring redis over here I'm going to move it out of here and move it inside of the add caching method now I need to replace builder.services with just services and I have an issue here where I have a dependency on the eye configuration to get the connection string the simplest way to solve this would be just to expose the eye configuration as an additional argument to this method so that's what I'm going to do let's add the iconfiguration argument and now I can use it here to get the connection string for redis at the end of the method I'm just going to return a service collection instance and I want to go back to program.cs and take out one other service which is the memory cache here I'm also going to place it inside of the add caching method instead of Builder Services we just need services and this is looking all right and now since I removed this code from the program.cs I need to remember to go here and call the new method that I defined so Builder services and we call the add caching method that we just defined it requires us to specify the iconfiguration instance so I'm going to pass that in also and this is how you can move your logic for configuring Services outside of the program.cs file let's add another method that's going to wrap this Logic for adding the infrastructure Services let's add a new method here to wrap that code that we just replaced I'm going to call this method add infrastructure so let me just create it and infrastructure it needs an i service collection to be an extension method and let's paste in the code and I need to replace Builder services with just services and then I return these Services instance so now I can go back to program.cs and let's for example chain a call right here to add caching we need to call add infrastructure now I want to take all of this logic right here for configuring mediator and the validators and these are application Level concerns so I'm going to add a new method that I'm going to call add application similar to ad infrastructure so let's go ahead and do that so public static iservice collection add application I need to Define service collection and paste the code that we just removed and instead of Builder Services we just need to use services from the add application method let me fix this in two more places all right we need to import the methods that we're using from other libraries and we return the services instance now I go back to program.cs and here we chain a call to add application let's take this Logic for configuring the database context and perhaps we can move it inside of the add infrastructure method if you feel like it doesn't really fit there you can always move it to a separate method that you can create but for Simplicity I'm just going to place it here so replace the Builder services with Services coming from our extension method you can see that we are missing the eye configuration instance here so that we are able to fetch the connection string so I'm going to fix that by adding an argument for the eye configuration instance so let's add that as the argument now that we have our configuration instance we can use it here to get the connection string and since I'm using the connection string just so that I can pass it to the use SQL Server method I'm going to get rid of the variable for the connection string and pass the configuration value directly let's go back to program.cs you can see that the add infrastructure method is red because we need to pass it an instance of iconfiguration so that it can fetch the connection string for EF core so what we have left here you can see that here we have some code for configuring our background service so I'm going to take all of that and place it in a separate method here that I'm going to Define now so let's add a new method which I will call add background jobs so I'm going to Define it with public static I service collection add background jobs and it needs to be an extension method and let's paste in the code that we took from the program.cs file so here instead of build Android Services we just need to use services and here also and at the end we just return the services instance to complete our extension method and now I can go back to program.cs and chain a call to add background jobs you can see that we have two distinct parts of configuration left this one is for our controllers and Swagger and this one here is for the authentication and authorization services so I'm going to add two more extension methods to wrap all of these methods so I'm going to replace this first for configuring the controllers and let's add a new extension method here I'm going to call this extension method add presentation so let's define it so add presentation here and paste in the code that we just took from program.cs replace Builder dot services with just services and we return the services instance at the end of the method and we go back to program.cs and let's say right after calling add background jobs I can call add presentation what we have remaining is the authentication and authorization services so let's take out all of those go back to the dependency injection class and let's add a new extension method this one I'm going to call add authentication and authorization so public static collection and authentication and authorization and let's make it an extension method so this iservice collection services and we paste in the code that we took out from program.cs we replace Builder services with just services so replace all of those I'm going to speed this up a little all right and let's return the services instance at the end of the method all right so now I just need to use this method inside of program.cs so right here after add presentation I'm going to call add authentication and authorization you can see our program.cs is much simpler now and our Logic for configuring the services is now just a few method calls and all of our code for configuring the services with dependency injection is nicely encapsulated inside of these extension methods this is the approach that Microsoft uses inside of the framework itself and it's also the approach that you're going to use if you're building any kind of library for others to consume I want to highlight what I think is a potential issue with this approach and even though it's a minor issue I think it's worth talking about as your project grows you're going to end up adding more and more extension methods to encapsulate all of the services that you configure inside of your application so that's one thing and the other thing is you have to remember to call that extension method in program.cs otherwise your services will not get configured properly so I want to show you a different approach for how you can Define your service configuration and have it applied automatically I'm going to start out by adding an interface to the configuration folder and I'm going to call it I service installer so I'm going to add that interface now inside of this interface I'm going to Define just one method it's going to be void I'm going to call it install it needs to have two arguments the first argument is going to be the service collection and the second argument is going to be an instance of eye configuration so let's go ahead and add that and now I'm going to use this interface to define the service installers that I need in my application the approach I'm going to take is I'm going to add a service installer for each of the methods that we have inside of this dependency injection class so the first one is going to be the caching service installer as I said we need to implement the iservice installer interface so let's go ahead and do that and if I go back to the dependency injection class I can just take the code from here and paste it inside of my service installer now let's go ahead and add all of the other service installers so the next one is going to be the infrastructure service installer let's quickly add that class so infrastructure service installer we implement the I service installer interface and we paste the logic from the dependency injection class so that would be all of this and we move it here we need to import the use SQL Server from any framework and we are done now we need to add the application service installer so let's go ahead and do that so application service installer we implement the I service installer interface and we just copy the code from the add application method so all of this and we paste it here to complete our implementation now we have the background job service installer so let's go ahead and add that background jobs service installer we Implement again the I service installer interface and we take the logic from the add background jobs method and we paste it inside of our service installer all right that's looking good and I think we have one or two more methods so the next one would be the presentation service installer let's go ahead and add that so presentation service installer implement the iservice installer interface and we take the code from over here and move it to our presentation service installer and the only one that's left is the authentication and authorization you can make this two service installers if you want one for authentication and another for authorization but I'm going to make it one just to make it simple so authentication and authorization service installer again we implement the iservice installer interface and we take the code from the dependency injection class and paste it into the implementation of our authentication and authorization service installer so this takes care of implementing our service installers and now all we need to do is take all of the implementations of that interface instantiate them and call the install method the simplest way to do that is going to be using reflection I'm going to add a new method to the dependency injection class that is going to apply our service installers I'm going to give it a name of installed services and it has to be an extension method on the iservice collection class and additionally I'm going to give it an argument for the eye configuration interface so let's go ahead and do that what we have to doing here is scan the current assembly and we can even pass in the assembly as an argument I'm going to actually do that so I'm going to add an assembly here as an argument and what you can actually do to make this even more flexible is to make this an array of assemblies and place the params keyword in front so that you can pass in assemblies one by one I'm going to call this argument assemblies so what I'm going to do now is iterate over the assemblies using link I'm going to find the types that implement the iservice installer interface and I'm going to instantiate them and then we're going to call the install method on the instances of the service installer so I'm going to say assemblies select many and I'm going to take from each assembly all of their defined types now I want to filter the types for the ones that are implementing the iservice installer interface I'm going to define a local function to check if a given type is assignable to a generic type that I specify as the argument and I'm going to use this as the argument for the wear method so I'm going to say is assignable to type and I'm going to specify I service installer as the generic argument here and now that we have filtered out all of the types and we have the ones that are assignable to iservice installer we can instantiate them all of the classes implementing the iservice installer just have a default Constructor so I'm going to call Select and I'm going to pass in activator and quality create instance method and this is going to instantiate my service installers but if I take a look at the result it's returning an innumerable of object so what I can do is cast that to the iservice installer interface now that we have a collection of service installers let's go ahead and introduce a variable that's going to hold our service installers and all that's left for us to do is iterate over the service installer instances and for each of them we just call the install method so I'm going to say service installers for each and then for each service installer we just call the install method we're going to pass in the iservice collection and the iconfiguration instance chances and this takes care of installing all of the services that we have placed inside of our service installers and I'm just going to return a Services instance to satisfy my method definition and now we can go back to program.cs we can get rid of all of this code here that we previously added for configuring our services and I just need to call install services and pass it an instance of eye configuration and we also need to specify which assembly we want to scan for the iservice installer implementations the simplest way to do that since all of these services are defined in one assembly is I'm going to say type of iservice installer and take the assembly from there so this is going to call our extension method instantiate all of the service installers and configure our services with dependency injection don't forget to smash that like button and also subscribe to my channel so that you don't miss any of my future videos and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 36,935
Rating: undefined out of 5
Keywords: dependency injection, dependency injection c#, dependency injection explained, dependency injection asp.net core, dependency injection configuration, dependency configuration, dependency injection .net, dependency injection .net 6, dependency injection asp.net, dependency injection web api, dependency injection services, dependency injection lifetime, transient, scoped, singleton, asp.net core, service lifetime, service collection, clean architecture, .net, .net 6, .net 7
Id: tKEF6xaeoig
Channel Id: undefined
Length: 16min 22sec (982 seconds)
Published: Tue Dec 27 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.