Why I DON'T use MediatR in ASP.NET Core

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
making a goddamn meat eater video Welcome to the Rock coding YouTube channel my name is Anton and today I'm gonna teach you how to take your mediator solution and write it without mediator I released my Wolverine video and everybody thinks I hate mediator I don't hate mediator you write your code however you want I personally don't see the point in using mediator unless you have a team that doesn't know how to structure code mediator is a cookie cutter solution that you can just apply to your code and it will have some good structure to it before we get in again I will restate the purpose of the video is not to dunk on mediator not to dunk on people that use mediator you can do it if you want to the purpose here is to show you that there is a different way to write applications in mediator-ish style and really I want to dunk on people who are using mediator but blindly and are religiously defending it so with that don't forget if you're enjoying the video like And subscribe if you have any questions make sure to leave them in the comments section I have a c-sharp course that is out go ahead and check it out let's go ahead and get started if you want the source code you will have to support me on patreon that is the unfortunate reality and here we have a program CS I built up this example during stream the asp.net core application is here we have some test requests you will see them in a minute but all the mediator stuff in the database is separated in the services project this project is really an optional step and the application is really simple we're registering mediator we have fluent validation our database we have our validation middleware and then we're listing and creating posts nothing too complicated here I decided to skip updates because hopefully this should give you a good enough idea of how things go lists post is pretty self-explanatory I just list stuff from the database create post as the command is really something a little bit more interesting we have three different types of behaviors two of which I personally think are sometimes pretty bogus the logging Behavior so you start log and then the next request we have the validation which is a one liner and then the add user ID Behavior which is really something maybe that you want to incorporate so you have this command or message execution Pipeline and then you have the Handler that is executed let's go ahead start up the application and see how it works and then we're gonna go ahead and rewrite it the application has started I'm going to open up the two requests that I have over here the first one is the lists post I'm just gonna execute it and here I have a couple of posts then we have a create post I'm gonna run this it's going to create a new post so if I rerun this there is the additional post if I try to and by the way the user ID is hard coded it's automatically attached if we have authentication it's going to be attached so let's just not go there I'm going to remove the title to show you that the validation is working in the middleware right over here I'm not remapping anything I'm just saying whatever validation exception occurred just go ahead and return the errors from there so that is why you see a big blob like this otherwise what you would actually maybe grab is these three things over here and there it is so basically a very bog standard example we're gonna go to the solution we're going to create a new project asp.net core we're going to call this custom because if you're not using a mediator to get this sort of behavior of a Handler going through a pipeline and sorry not the Handler going through a pipeline a message going through a pipeline and then the Handler being executed on the end you're gonna need to build some custom Behavior so first of all let's take the designer across I will need to grab my database so I'm just going to put this over here obviously can't forget the item group so let's just put all of this here fluent validation I will still need this from the services I'm gonna grab SQL Lite I'm not gonna grab a mediator but because well this is our custom solution we don't need mediator with those things ported across I think that is about all I will need from the program CS of the app I want to go into programmer CS of the custom right over here I will just take everything Port it across I will need the database and the database currently exists in Services I'll just take the database and I'll put it here I will also take the migrations and I'll put them here I will say a bunch of stuff missing in here and I will need to change the namespace so for now let's take posts we are going to migrate them as well the behaviors we will leave them there but we will take them with us at some point as well so first of all for the namespace let's go ahead and do a global rename of namespace to custom in the current project which will be the custom one we'll do replace all this should fix a bunch of namespace issues database let's go ahead and import it from Custom database everything else let's go ahead and delete it we don't have mediator the validations are going to come from this assembly so program and by the way some people like it some people don't I like to take my validation and slap it on onto the same class doesn't really matter I sometimes even do this where I take my command and I place it on the Handler itself okay again I think it's a preference thing I think keeping these things together Inseparable is slightly better let's quickly visit the migrations and just make sure that nothing is breaking here I'm not sure why it's complaining it will be the using statement over here and then the using statement over here again let's close everything and go to the next error again the using statement over here we'll then go to the posts posts works as expected we don't have the request so let's actually just go ahead and delete this the posts over here in the database will be missing a using statement let's get rid of these over here this should be working now one bad using statement over here we'll then go to create post again remove the interface implementation the iuser command I feel like we'll still need it for the user ID so let's actually go to the markers we will take this interface over here and I'll just put it here again you can put this somewhere else you can have your common interfaces stuff like that I'm just going to place it here to mark this up as something that I should be attaching a user ID to or the errors that are happening in here again it's mediator and the behaviors for some reason was on there closing everything again and now in program CS we want to try to execute our handlers now our handlers are pretty simple for the list let's go ahead and just bring the database in here DB instead of mediator we're gonna have a list posts DB inserted so list posts should be here it's called the as the command and the Handler is in here so it's really the query post Handler let's import this there is no sand we just have the handle and then we Supply the list posts and we also want the cancellation token so let's actually bring the cancellation token in here CT and pass it as the last parameter right over here and there you have it right I would say not too complicated at all you then go to create posts here is your command here's your Handler so same thing again you want to take your Handler you want to put the database in there and I know maybe somebody is like Anton what are you doing this looks suspicious as hell don't worry we'll get there the handle the command and again the cancellation token but the cancellation token on the end there new line put the brace on new line as well so it formats nicely and there you have it so this basically removes a mediator and instead of invoking the handle behind somewhere you are basically just bringing it to the front and executing it directly now there is a big point about some crazy complicated reflection that you can write over here to load these things with the dependency injection container for example if you basically just check your program class and you will check the assembly and you are just get all types and you're gonna say Okay I want all types which the name ends with I don't know we have both of them named with Handler so let's just say we will name they will be named to a Handler and on the off chance that it's an abstract class and it's an interface we don't want those so let's go ahead and just negate those real quick over here right so for all the classes that are going to be returned as part of this you're going to have this type well you just go ahead and register this as a transient type right and then everything that's going to match is going to be injectable right so you can actually just take this handle and register it here or even call it Handler okay so do something like this I know this is a pretty complicated for some people to understand or to grasp as that you're trying to look at all of the classes in your current thing and that they are not abstract or interface really hard to write you need to be at least five years experienced senior developer to do that but don't worry I assure you if you practice enough you will be able to do that let's take the Handler place it over here and there we go okay so we managed to do some senior developer stuff over here and over here and I know some people will still be up in arms about this because how can this possibly be any better and where you can actually just understand which function you're invoking over here whereas in mediator again it's not really a big problem you instead of saying where you send the thing you just say which command am I sending okay and this is where it's going to be processed okay not a big deal I would say the bigger ordeal is if you have this great post over here uh which behaviors has it actually went through at that point you might want to inspect all of the behaviors that you have and what effect it had on the request if it did mutate it Etc believe it or not if you're rolling your custom solution and you don't go too crazy on hiding stuff away the pipeline that you're invoking can be pretty apparent as well so first of all let's address some kind of behaviors that you can have the logging Behavior this can be a pretty fun one I think it is okay to just put it in your middleware sometimes remember that the pipeline you're building up over there is pretty much middleware or sometimes you're just replacing middleware right with minimal apis you don't actually get information about the full object which is being supplied to this function so you can't do validation here so we will need to have some kind of pipeline but let's say we will have our request Services we will get our required service this is going to be an eye logger of our named middleware but let's say we'll just call it program here okay like logger place it over here over here oh gosh which end point are we executing it's this endpoint damn and as long as this endpoint isn't null uh let's go ahead and execute this otherwise let's say text oh my God I think people will be more angry about me using the else keyword than not using mediator but anyway let's take endpoint we will get the display name over here over here and I think this is looking quite a right now for the hard stuff for the really hard stuff we have some kind of behavior let's say add user ID behavior and then we have validation Behavior the post that we're creating is actually validating the user ID so this user ID Behavior has to run before this validation Behavior if we take a look at this create post we will know that this stuff is reconfigured somewhere in the program CS right as long as you don't care about how control flow happens in your application I think this setup is pretty good again let's close everything and we're just going to focus on the add user behavior and a validation Behavior if you you understand functional programming right you're gonna understand that in your custom logic you want to execute some kind of function and before you execute that function you want to run some more functions so what you do is you create a class and you say this is a user command pipeline right you give it a name because now you can have many pipelines and you can invoke them whenever you want in the Constructor you will take your actually let's make it a surprise well we'll leave it for later right we'll just say task uh we'll call this method pipe why not and this is going to have to return something we don't know what it's going to return so let's say t response and it's going to have to accept a t Command right or a t request even so let's say t request so T request in and then T responds out respawn is the wrong spelling by the way so we have the request and we can even go as far as saying that the T request will be an i user command which I think is pretty cool the next thing is we actually want to take this function that we have over here right which should pretty much be the same and conform to some kind of standard that we have one parameter in the cancellation token is always gonna basically follow in and then we have the taskable if you have synchronous functions believe me you will be able to implement those as well so T request we have this over here but the function signature basically looks something like this right you have the T request coming in you have the cancellation token but then you have a task response of T response right over here there right so for this to fit on my screen I'll actually format this so this is going to be the Handler that we want to execute and I'll just need to close off the type and then you actually want to pass the cancellation token believe it or not when you're using mediator you actually want to inject your cancellation token here and pass it to the send if you do not pass it to the send you are going to get a cancellation token none supplied into your Handler okay so if you want you can pass it if not you don't pass it but here is how things are going to go you are going to take the Handler you're gonna execute the request you're gonna execute or put the cancellation token writer is really not good with passing the correct thing if cancellation token to type in the variable have the same name so we await this is asynchronous there is going to be some kind of result over there and we return it let's check out the add user ID Behavior this is one line here but generally you will want to get your HTTP context or claims principle and assign it here remember that we have the type check over here so we're capable of just a signing it okay now with the validator let's take a look at this we have the validation Behavior and the validator because we have the generic type over here when validation behavior is being instantiated that's when it's capable of figuring out which validation service it needs we don't have that kind of luxury so what we're going to do is we're going to place this logic over here and we will just do similar type of work right so we're going to get an eye service provider this is going to be an SP I will say that we want a service so get service off type I validator tier request as long as that's registered and this is going to give us a validator if the validator doesn't equal no maybe in one pipeline you want to enforce it in another pipeline you don't want to enforce it but this gives you the ability to set up whatever pipelines you want mix and match them and execute them however you want because you can actually specify them in place you don't have to do things based on type matching right and because this is so easy to set up why wouldn't you just create a new pipeline for whatever purpose that you want right so take the validator place it here cancellation token we got that and there we have it this function over here we no longer needed so this is a user command pipeline we're gonna come back to our L10 Amazon engineer code over here and we're gonna say if we end with handlers or pipeline right we actually want to grab it so let's put this over here we're gonna say pipeline format the code and there we go so that should actually automatically pick up this type register with the dependency injection container and then it's basically just usable so this user command pipeline we just go down to the endpoints wherever we want to use server opposite here you have the pipe you take the pipe you pipe the command to the Handler handle and then you put the command the dot command okay the cancellation token in the end and basically there you have it don't think I've forgot anything if I did forget anything I mean we will find out so let's go to custom dot watch started up so Port is 50 55 so list posts let's say 50 55 we're gonna execute this we're gonna get the two entities that we have here let's go ahead and execute this unable to connect because I didn't set the port so 50 55 looks like we've managed to save that let's run this again and there is our other user so there you have it program CS if you have some kind of command that you want to execute you know exactly what kind of generic workflow it is going to go through and where the Handler is going to be executed in there and by the way if inside this pipe what you want to do is put more pipes or put reusable pipes a first of all potentially they're just going to be simple static functions that you can place here kinda like this validate and throw away sync if you indeed need to mix and match pipes it's basically going to look a lot like the logic over here right you're just going to have a pipe that's inside another pipe and you're gonna invoke stuff before it and stuff after it and that is pretty much it I'm gonna collapse the custom over here we're gonna go to the benchmarks we're gonna take a look at mediator so first of all let's talk about code volume this is what we have in here the I valid dateable the iuser command the two behaviors and then the create post Handler all of this comes down to 71 lines of code if we go to custom we take a look at this exactly the same stuff we still have the iuser command here we don't need to say that something is I validatable I don't have a package over here so I have to do some pattern matching on one line and this comes down to 60 lines of code okay so I removed a lot of stuff like the implementation for the Handler basically 10 lines of code so the code mass is smaller if we go to program CS again I mentioned it not a big problem you can just go to to create post and then because these are going to be in the same file you can see the control flow the hard part is to figure out the control flow you have to take a look at which command you're executing which pipelines are registered and then you have to look at each individual pipeline to make sure that it is triggered on the correct command that you're issuing okay here if you have this kind of implementation the control flow is apparent right so you have this thing you can read it you can then pop back out and then you can actually take the handle and see which handle is being executed right so you know what's happening so if we go ahead and run this in release configuration the results are in your code will be slightly faster as well and slightly more memory efficient if you care about that sort of stuff and that's pretty much all for this video thank you very much for watching hopefully now you can hold a different perspective on how to build command handlers and how to implement this pipeline which is just function calling another function all in all you don't have a dependency your code mass is smaller your control flow is more understandable and you're slightly faster these are the reasons I don't use mediator I don't judge other people who use media if you want to use media go ahead and do it just make sure you're not doing it blindly I believe everybody can write good code themselves that fits their way of thinking and you don't need to rely on somebody else going and saying this is the best way to write code you can actually take a look at your own domain write the code decide for yourself is this working for you or not if it's working for you great that's the bottom line it doesn't matter what anybody else says don't forget if you enjoyed the video leave a like And subscribe if you have any questions leave them in the comments section or come ask them on my Discord server if you would like the source code for this video as well as my other videos please come support me on my patreon your help will be much appreciated a very big and special thing it goes out to my current patreon supporters you help me make these videos as always thank you for watching have a good day
Info
Channel: Raw Coding
Views: 10,783
Rating: undefined out of 5
Keywords: csharp, c#, asp.net core, .net 7, MediatR, minimal api
Id: gIVtrBtR-Yw
Channel Id: undefined
Length: 21min 50sec (1310 seconds)
Published: Sun May 14 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.