BrandGhost

Beginner's Guide to C# Dependency Injection in ASP NET Core with Needlr

If you're new to working with ASP NET Core, you've likely already been using dependency injection but not even realized it. Want to understand more about how it works and how to register and resolve services? Let's dive in, and in the end, we'll see how Needlr can simplify some of this for us!
View Transcript
We can literally finish the whole thing by doing this. In this video, we're going to walk through an introduction to being able to use Neler for registering dependencies to create an ASP.NET Core web application. Now, that sounds like a whole bunch of stuff, but really what we're going to be focusing on is really simple dependency injection. And if you're not even sure what that is, no worries. We're going to start pretty lightweight and we're not going to get into all the nitty-gritty details. just enough that you have a high-level understanding of what's going on and how you can leverage that for your web application. So, let's jump over to Visual Studio and walk through this. So, to kick things off, I am in a solution that I've already created and I have a project open which is from my Needler Nougat package. But what I'm going to do is just kind of quickly show you here that I have two projects that I'm depending on. You're not going to want to do this, right? I have these projects because I have the code cloned. But what we're going to do is reference these Nougat packages. I'm going to go over to the project in the left hand side and I'm going to pull in two different Nougat packages that I'm interested in. And just to again show you the Nougat packages we want are the needler ASP.NET one and then we also just have the base needler package. You might not even need to pull in the base needler one. So, just to show you, going to have include pre-release checked because at the time of recording, Neler is just a alpha stage package that I'm putting together. For what it's worth, I'm going to be walking you through how to use it, but you don't have to use it. I'm just going to use that as a guideline for explaining how dependencies get registered here. So, if I search for needler, okay, and there's a bunch of different packages. So, not to overwhelm you at all, but we're just going to take the ASP.NET net one. The other one that I mentioned is just the base package, but this uh this ASP.NET one should include it by default if you look on the right hand side here. So, we're just going to take this one and install it. Cool. So, if I head back into the project, you will see that well, this all got screwed up and that's totally okay. We're just going to take this out. The comments got a little bit messed up here. There we go. This is using central package management. So just one more concept to quickly explain to you that if that's not something you're familiar with, this usually has a version associated with it. But when you use central package management and I head over here, it gets included in this list. So you can see right across here I have the needler ASP.NET package pulled in. If you don't have central package management, you'll have basically this entry directly inside of your project over here. So, we'll get that out of the way. Now that we have this package pulled in, let's talk about building a web application. The way that we normally do this is that when we're building a web application, we have to make a web application builder. So, there's going to be a lot of builders and factories and that kind of stuff going on here. But the idea is we make a web application builder. Then, we configure things on that builder. And that means that we can add in different services. And like I said, we're not going to go super in-depth, but there's a whole lot that you could do here. There's other Nougat packages that you can add in that add fancy functionality. There's authorization, authentication, there's logging. There's all sorts of things that you can start putting onto your web application builder. You might be saying, well, why would we do that? What's that going to do? Like, why go onto the web application builder? But the idea is that this is going to enable dependency injection for us. And that means that when we add these things onto the web application builder, they're essentially going onto the service collection of that web application builder. And that means later on when we need to resolve different instances of types, we can actually ask the dependency container, hey, do you have this thing registered? And if it does and it's able to construct it, it will give us the instance of the thing we need. So just a super quick example, you want a logger. Do you have to say new logger every time you want a logger? No. We can actually ask the dependency injection service for that logger. And even better, we don't have to directly go to the dependency injection service and say service provider.get logger. We can use the dependency injection itself to create an object that automatically has that logger passed in. Once you start seeing how this stuff comes together, it's super powerful because you'll stop manually passing in object references into each other all of the time. It doesn't completely go away. It just simplifies things a great deal. So, that was a mouthful, but I just wanted to give you a little bit of background for what's going on. Generally, how we would go do this is we would start with a web application builder. So, we would actually do web application.createbuilder and then you can pass in there's arguments and stuff you can pass. We're not going to worry about that for now, but we would say builder equals this new web application.createbuilder. I'm going to try not using var as much as possible just to make this very clear. Otherwise, you might not know the type of that as we go through this. Now that we have a builder, really, if you didn't want to configure anything, you could just say build right on here. And I already tried doing var again. Then it's just these two lines. You have a builder, you ask it to build. Now we have the web app. And then you would do web app.run. Okay. And you can run async or anything like that. But this is really the minimum you need to have a web application that gets put together, but it's not going to do a whole lot of anything. So we don't have different endpoints that you can hit. There's no services configured on here like logging or background services or anything else that's going to be happening. So this doesn't really do a whole lot, but this is really just the basics of what's going on. You might have noticed as I was typing though, if I go back up here, Copilot might already start to sort of fill this in, but you can see builder. Right? So remember I was saying service collection, service providers, dependency injection is to resolve services, right? So if I just press tab to let this kind of give us an example. What we're able to do with dependency injection is we can say that we want to go add some different services onto this service collection. If I hover over this, just so you can see the builder.services property is a service collection. Now, for a little bit more background, before we build this thing, we're actually going to be dealing with a builder that has service collection on it. We register things onto this service collection. And that allows us to say we want to configure all of these different services we're interested in. Right now, this is with this one line is going to be just some things that enable the API explorer. Okay, it's going to have some types that it's adding onto there, but we can keep adding more stuff on up until the point where we call build. This build line is building the web application, but behind the scenes, it's also going to take that service collection and convert it into a service provider. When the service provider has been built, you're not adding anything else onto it. It's already built. But the cool thing at that point is you can say, "Service provider, I would like a whatever." And it will go get you the whatever you're asking for if it has been registered in a way that it can resolve it. So, I just wanted to show you an example of this quickly. And we're just going to make up something kind of silly. We're going to have Nick's type here. It's not Maybe we could just make it say hello world or something. So, I'll have it print hello world. All that I'm going to do is go up to here, builder services. You can see that it added add singleton. I'll explain this in just a moment. It did add singleton and then it provided the type nick type. When we do add singleton, this is describing the lifetime of this object that we're working with. Add singleton means that we're adding the type n type and anytime someone asks for that, it's going to return the same instance of it. There's can there can only be one instance of it. It's a singleton. And how do we do that? Well, let me go move this run line down here. But on the web application, there is services again. Kind of like what we had up here on the builder. But notice this builder one, if I show you with my cursor hovered over it, it says I service collection. And down here on the web application, once it's been built, it's a service provider. I cannot do add singleton on here. Right? You can see that Visual Studio is not letting that happen. But what I can do is get a required service or get a service. If it's required and it's not there, it will throw. And if it's not required and you ask for it, you'll get a null back. But I'm going to do this. And let me do I'll make it a little bit more verbose. Sorry, I called it Nick's type. That's right. My camera's in the way of my face. But um we're going to do this on multiple lines here. And then I can say, right, let me just rename this to make it more clear. And I put keyed there because my camera's in the way. Let's get rid of the keyed. That's a little bit more of an advanced topic that we're not going to get into right now. But essentially, if I were to go run this right now, if I press that, let me set this as the startup project. I'll press play on that. What we should get is that we've now registered a type onto the collection. We build the web application and then we're able to resolve that type. So, it did run. It didn't throw an exception. And you can see that if I go check the console, it says hello world. So this is right before I've actually told the web application to run. If I press F5 on that, now the web application is running. You can see that it actually launched the browser to try going to where the launch settings tell it to. But there is no endpoint there called weather. So that's not going to do a whole lot. But you'll notice now that I printed hello world that's coming from nickstype. Do something. And then you'll also notice that there's a bunch of other stuff on here and that's because the web server is actually running. So that's this line right here with web app.run. So far I hope that makes sense. And you might say, well this seems like it's overkill. Like why did we have to go add the singleton on here next type and then why did we have to go resolve it? Why didn't we just do type? Why didn't we just new up a new instance of it? And you're totally fair to ask this question because in this example so far, I mean, that's way shorter to go do this. That's way easier. We don't need to use dependency injection at all for that. But let me show you one more quick thing just to illustrate how we can start passing things in automatically through the constructors. And this will be constructor dependency injection. So what we're going to do is make a new type called Nix dependency. This isn't going to do anything. We don't need it to do anything. It's just to illustrate the point here. I can say I need nyx type requires that we have this passed in. Okay, we can say this type now requires this dependency. So we can't make a new instance of Nick's type unless we pass that in. So now you can see on line 9, it's upset with us. It's going, you can't do that. We need Nick's dependency passed in here. So well, you know the answer for how to do this. We'd say nyx dependency. We'd go new it up and then we would put nyx dependency inside here. And there you go. Still not too bad. But you can imagine that as your application gets progressively more complex that all of a sudden if you have to keep chaining these things into each other, it just gets worse and worse and worse because now you have a lot of dependencies that are being passed into each other. you might need the same complex set of things instantiated and then passed into several different spots. With dependency injection, we don't have to worry about that at all. You'll notice that if I run this, I'm going to comment this part out. And I just want to show you that we can essentially go run this, but we still have to go register the dependency. Nyx dependency up at the top. Now, now that the dependency container has both the dependency and the type, what it's able to do is create Nick's type for us because it will be able to find Nick's dependency. Okay, so just by running this now, this succeeded. I'm going to go show you in the console here. It still says hello world. Again, nothing very exciting and thrilling, but we didn't have to change anything here. That means that anytime we want to use a service, we can just ask for it and if the dependency container has the right things registered on it, it will figure out how to go build all of those things for you. Now, what I want to do is stop this because I realize this example is not that awesome, but this is where the concept of needler might start making a little bit more sense. So, what I want to do is extrapolate this a little bit further. So you can see how when we go to introduce Neler and why I built it. Hopefully it makes things a little bit easier or a little bit more lightweight. Again, I'm not telling you that you have to use it. This is just something that you could consider. You could build your own thing. You could do this a million different ways once you understand how it all works. In this example so far, we have two types that we're dealing with. We were explaining that we don't necessarily want to be in the business of constantly newing these things up and passing in the dependencies ourselves. That's what the dependency container is for, right? What we can do now is we can say, well, this is a web application. So, if we have a web app, let's go put something onto here. So, we have a little minimal API. Okay? So, this is just going to be at the the root. And then if we go run this now, it's going to print hello world. Has nothing to do with our type up here. So we can actually just comment that out. This application right now has a couple of things registered onto it that it's not using but it also just has this minimal API. I go run this just to show you again very simple example. We'll get rid of this break point too. There's nothing at weather. But if I go to the root it says hello world. Okay. So again not super thrilling but these are just the stepping stones that we have to go through. Let's play around with some dependency injection now. So this is a very simple minimal API. It just says hello world. What if we changed Nick's type and what if we changed the dependency up as well. Okay. So what I want to do is I'm going to say Nick's dependency actually has get message on it. Okay. And it's going to say hello from Nick's dependency. Thank you co-pilot for helping me be creative on the fly. So now what I want to do is change up Nick's type. We're actually going to use this dependency. So, I'm going to keep a copy of it inside here as a field. And then instead of do something just printing it to the console, I'm going to say that we're going to use let's do a string builder just for fun. I'm trying to see if Copilot will get a little bit creative with me. Hello from Nick's type dependency get message and we'll return that. Excellent. What we should see if we wanted to use Nick's type now is that if we said do something, it's going to give us a string that says hello from Nick's type first and then it's going to use the dependency to get the message from it, which if we jump down to it, right, it's just hello from Nick's dependency. Again, simple stuff. We're just chaining these things together and making sure that Nick's type is really going to use Nick's dependency. But how can we make our minimal API a little bit more fun? Well, let's put Nick's type on here. And then what we can do is this. So now we can say, and let me just put this onto another line, make it maybe a little bit more readable. Now we can say that we have this minimal API and when we call it, we want to use nyx type and say nix type do something. And we're just going to return back to the caller, the browser in this case, whatever do something says. But how do we get next type? Well, that's all thanks to dependency injection, right? So, if I comment out these lines up at the top and we go run this, what should happen? We get a big scary error, but it actually tells us what's going on, right? So, if we read through this, it says the list of parameters that we found and it's saying Nick's type and it's saying that it's coming from the body, but it's inferred. But then it even tells us, did you mean to register parameters as a service or apply the from service or from body attribute? What's happening is this is ASP.NET going, "Hey, look, you're asking for this thing, and by default, I'm looking for it in this spot, but I can't find it there. So, are you sure that you're doing the right thing?" And the answer is, well, kind of. We kind of are. This part technically works, but we're missing the dependencies. So if I put these back on again, the builder will have its service collection populated with these services. When we build the web application, the service collection gets converted into a service provider. And then minimal APIs are able to resolve these instances of these types from the service provider. So let me go run it again. And now that we have these things on there, we should get this to work. Okay, so we see hello from Nick's type and we see Nick's dependency. So the only reason that we can see these things, the only reason we can see them is because these services are registered on the container and then the dependency injection framework is passing in an instance of Nyx type so that we can call do something on it. So far so good. Hopefully that's all making sense. We're scratching the surface of dependency injection. But now you're probably saying, "Wait a second. I came here to learn about needler. This is a needler video, right? Well, kind of. It's a dependency injection introduction and some needler. So, now that you see how we start registering these things onto here, what I want you to do is imagine that if we keep building up a more complicated web application, this list of services that we want to add onto here continues to grow. Okay? So, as you add more features on every time you're making a new class that you need to be passed in through dependency injection, you come all the way back here and you keep adding stuff onto this list. With a really simple application or something that's not changing too much, great. Not a lot to worry about. But if you're building something that's growing and getting larger scale, odds are this list is going to get pretty nasty. And what happens is generally people pull these things out into extension methods. And that's just a way that they can group some similar registrations together. So they bundle all these things up into an extension method. And that's where you end up with things like you saw where it's like builder services and then this add API endpoints explorer. Just to show you, right? If I jump into the code for this, it's not my code, but let's go see it. You can see that this is an extension method, right? Right? If you're not familiar with extension methods, not to worry. But you can see that this is defined as an extension method. So this keyword right here and the fact that it's static allows us to basically make it look like we're adding a method onto services or an I service collection in this case. So it lets us kind of reverse how we call this. So we can say services dot addend endpoints API explorer. Right? You can also just call add endpoints API explorer and pass in services. But the whole point with extension methods is that it lets us write it this other way. It looks nice, right? It's syntactic sugar to make it pretty and more easy to read. Generally, people will go pull stuff into these extension methods and that way you're not polluting everything inside of this program.cs file. You can kind of go move that pollution somewhere else and kind of separate it all out. It's a way to help manage and organize all of these types that you want to register. Now, we're going to talk about Neler and why Neler is my solution that I use in my own applications that helps me register things in a more elegant way. This is what I would call an opinionated framework. And what that means is that it's done in a way that I like to do it. Means that you may not like to do that. So, I want to be very transparent here, especially if you're a lot more advanced. And if you are, you probably aren't necessarily watching this video to go see how dependency injection works. But there are going to be things in Needler that you may not like and they may not work in your situation. And that's totally cool because a lot of the goal with me making videos showing you Needler is not to force you to use it. I'm trying to talk about how I build things, why I do it a certain way. And something that you can gain from that is maybe you don't like doing it that way. And that's totally cool. At least you got to see a different perspective. Okay. What we're going to do is start using Needler here. So, I'm just going to push this code down a little bit. And the idea is we're going to say using Nexus Labs Needler. And then we're going to pull in the ASP.NET part. We're going to pull in the injection part. Again, because this is a dependency injection framework, like some pieces that I'm building in here, I'm playing on the injection part. So, needler injection. We're going to be using a syringe to go build this stuff. The way that this works is I would say we're going to do let's call it web application. And I'm going to call it just so we have it a little bit more visible here. Okay. I'm gonna start by making a new syringe. Okay. And then we can literally finish the whole thing by doing this. The only thing that we would need to do in this case is pull this up here. And this will essentially I guess I have to run the app as well. Sorry, I got ahead of myself. But if I comment out all this stuff, the idea is that this is the equivalent code. So it's a little bit of savings. It doesn't look like a whole lot, but the key part that I want you to realize is that we got rid of this. The two lines that I have highlighted here, I didn't have to register Nick's type or Nick's dependency. And what should happen with Needler is that it will go looking for these things. To prove this to you, what I'm going to do is I'm going to run this. And what we should see is that we're only running this Needler web application now. And we should still see this stuff print out when we go to the root. So, let's go run this. And there we go. So, this still works as expected. So, I'm just going to go back in here. And one more time, I'm going to put a break point in here so we can see it. And I wanted to put the break point here so that we can see when I'm debugging it that we literally do have the type pulled in. You know, there's not magic going on. It's not like it's a stale cached build or something. This is truly needler that what it did was went and looked for the types to register and then it wired that all up almost the exact same way that you see here. There are some different conventions and things like that, but I'm going to explain that in just a moment here. And now that it had it all registered and we built the web application, it essentially just made all of this same thing happen. It's just that it's doing it a little bit more automatically. What do I mean by that when I'm saying it's going and finding things? So, the idea with Needler and a lot of the software that I build personally is that I like building what's called like a plug-in style architecture. And that means that I like having my modules very independent from each other. The entry point, like you're seeing up here in my applications, is usually extremely lightweight. In fact, I'm just going to extend this a little bit more. It's going to add some more code, but just to kind of show you what's going on is that instead of doing what I just had, I would actually go add a plugin. And the reason I would do that is I can go add a plugin to a new project, I can go add a plugin to a new file somewhere, however you want to separate it out. And Needler will go look for those plugins just like it's looking for these other types, right? Like I didn't tell needler specifically go register next type. I didn't tell it go register Nick's dependency. It went and looked for those things and said I'm gonna add them onto the container because it figured out that it knows or has enough information that it should be able to construct instances of these types. What I'm going to do is cuz I have on my clipboard that web API that we want to have, I'm going to make a new plugin. And there's different plug-in types and I'm just going to use one from the ASP.NET part of Needler. So, I web application plugin. There's a builder plugin where we can configure the web application builder. But because we're just adding this onto the built web application, we can use this type of plugin. And again, you're not going to see me register this anywhere. It's just going to be created. So, we can define the code and then Neler will go find it. When we start this up on here, we can take the options which have the web application. So this is now a plugin that we can go have somewhere else, right? This could be in a different file, different project as long as neededler can see it in the running directory, which by the way, this is a configurable thing. You could go have a plugins folder, anything like that. My entry point can remain this short and really just to kind of exaggerate it can be one line long. So let's go run this. And you can see that it went and found it automatically for us. So, we end up having the exact same thing that we had before. I realize that makes it not very exciting, but the point is that Needler could go find the plugin without us having to do literally anything using the syringe or any other part here. So, that is the basics behind Neler and dependency injection. Just to kind of reiterate on a few concepts, when we're building a web application, we have what's called a service collection at the time that we have the web application builder. Okay, so if I scroll back down here to this commented code just to highlight it, when we have the web application builder, we are dealing with a service collection. So if I hover over this, you can see service collection. When we have a service collection, we can go configure the dependency container, right? We can go register things in different ways. I talked about singletons. There's other scopes. We didn't get into them. There's scope. There's add transient. There's a whole lot of complexity that we can start getting into. I'm just walking you through the very very basics so you can see the concepts. But when we have the service collection, we're registering things onto that. Then we build the web application. And under the hood when it's building the web application, it goes and changes the service collection into a service provider. At that point, that means no more things get registered onto the provider, but we can start asking for things off of that provider. So you can explicitly go resolve stuff. So you can say get required service, right? You can do this kind of thing. We saw this earlier. or when you ask for Nick's type just like this, it will automatically go resolve Nick's dependency, right? Because it needs that to go build Nick's type. That's the idea behind the sort of the life cycle of building a web application when it comes to dependency injection. And then that means when we're calling things like minimal APIs and things that are sort of registered onto that web application builder for us, all of those things generally have some type of dependency injection support for us. And that's why our minimal API knows how to go find this. It knows how to do that because the service provider has that built into it. Now, that's how all of this part worked. Let's get rid of it because what we saw is that if we use needler, we can go define this stuff uh essentially like on its own. So like all these types are defined down here. This plugin we defined is here. I don't have to go explicitly put them onto our syringe, which is like our fluent builder that we're working with. By the way, when I use the word fluent, I forgot to mention this is fluent because we have this kind of syntax where you can chain things together. If you're used to seeing things like link code where you chain like wear clauses and select statements and things like that, this is the same type of idea. But you have all these extension methods where we can go configure how we want to go do this kind of stuff. But in our simple setup here, we don't really need anything fancy. So we have a oneliner that builds the web application and runs it. And behind the scenes, Needler will go look for plugins and it will go automatically look for types that it thinks it's able to construct for us at runtime. And that way we can clean up a lot of the boilerplate code for registering things. And that's why in my applications, I can go out and build lots of different plugins and I never have to worry about coming back to the entry point and doing a million lines of trying to register all the types onto it. So that's it in general. I hope that that was interesting. I hope that made sense. And of course, what I've been saying for anyone who's interested in Neler or dependency injection in general, if you have any questions, if none of this made sense and you need me to deep dive on certain parts, please just ask. I'm happy to go into detail and you can check out Needler available on GitHub. Like I said at the time of recording, this is an alpha. So check it out, go through the code, see what it's like. You can clone it down, mess around with it. It has all of these different sample applications in it for you to play with. And I hope that I'll see you next time. Take care.

Frequently Asked Questions

What is dependency injection and why is it important in ASP.NET Core?

Dependency injection is a design pattern that allows us to create loosely coupled applications. In ASP.NET Core, it enables us to manage dependencies between classes more efficiently, making our code cleaner and easier to maintain. Instead of manually instantiating dependencies, we can ask the dependency injection container to provide them for us, which simplifies the process and reduces boilerplate code.

How does the Needler package simplify dependency registration in ASP.NET Core applications?

Needler simplifies dependency registration by automatically scanning for types and registering them in the dependency injection container. This means I don't have to manually register each dependency in my application; instead, I can just use Needler to handle it for me, which reduces the amount of boilerplate code I need to write.

Can I use Needler in any ASP.NET Core project, and are there any limitations?

Yes, you can use Needler in any ASP.NET Core project. However, since it's currently in alpha stage, there might be some limitations or features that are still being developed. It's designed to be flexible, but I recommend checking the documentation and experimenting with it to see if it fits your specific needs.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload