What’s the Result Type Everyone Is Using in .NET?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody I'm Nick and in this video I'm going to introduce you to a very interesting and Powerful type slash Concept in c-sharp and that is the result type now I say concept because it doesn't really exist as a concrete type in the BCL it's not in.net but those of us who use it tend to add it either by writing it manually or introducing it through a nuget package now I'm making this video because in my mediator series you've seen me use that result type quite a bit and many of you have asked in the comments hey Nick what is this result type why are you using it where is this coming from can you please explain it so I'm gonna take some time in this video and explain everything you need to know about this type and why I like it so much if you like a lot of content and you want to see more make sure you subscribe ring the sub notification Bell and for more training check out my courses on domtrain.com alright solution that I have here and I actually want to start by showing you that result time that I actually hand wrote in that mediator video so the type in question is actually this one over here now just by look looking at it you can see instantly some things for example it is not actually a class it is a struct here and it has two generic type parameters one is the T value and the T error both of them can be here both of them are nullable and then we have two Constructors one for the good or first value and one for the error value and then we have an ease error or e-success flag and then over here we have a couple of implicit operators which are very very important for this concept and then what you need to know is that we have a match method that will match either success or failure now let's see how something like this would be used and I want to point out this is a very naive way of implementing it it's a very simplistic way you can actually take it in many other places to make it more optimal for what you're doing and we're gonna see those in this video but for now what you need to know is that if for example I want to see how a movie is created in this mediator Handler for example it comes in here and this Handler or any sort of method using the result type is a result of type movie The good path and validation failed the error path the bad path and you have two paths now this type technically is a union and this type is also technically a monad we're not going to explain what that is you don't need to worry about it the only thing you need to know and the most important thing about all this is that a result can only be its good state or its bad state it cannot be both States or no state so if we scroll down over here you're gonna see that we have this validation result and we send this movie Object to validate the movie because we have some rules for example a movie can have an empty title or it can be published into the future and so on so if validation result comes back as invalid we return a new validation failed class now this is just a simple class nothing special here but how is this allowed if we actually return a result here same with the movie we return the movie over here not a result of moving and here not a result of validation failed well this is where those implicit operators actually come in place into the picture because they will detect that this is for example the T value the happy path and automatically convert that movie type here to the result of a movie and this validation file type to that result validation so your code can still be clean and simple without much of the validation context in it and you can just keep it in the signature now why is that good well that's good because it's an errors like this where you have either success or failure in a very sort of binary way what you can have when you're consuming the result is this match method so I am matching over here that result type and if I just show you the five explicitly you're gonna see that this is the type of that result and then we're matching it and we're returning that I action result so we say if you are in the happy path if a movie was actually created then take that movie object that you created and return it here if not if you have a failure then map that to a bad request accordingly now like I said this is a very simplistic way of implementing it but it will actually do the job perfectly for something like this but what I want to do in this video is actually show you how we can introduce it and take a look at what's happening Behind The Sims and we're going to use a way more advanced result type in this video in fact I think the best result implementation you can have so over here what I have is the same sort of movie API context but in this case we don't have mediator we just have a simple service and when something goes wrong over here in fact let me find it with create and update we just validate and throw an exception and then this exception is caught in this validation exception middleware where we we write an exception so what's happening if I just quickly run this API and I go to postman to file request if I say go ahead and create Nick the Greek in 2024 which is a year after when I'm recording this we're going to get an exception of that exception is then translated into an appropriate contract and that's all happening with exceptions as control flow which is actually a quite bad way to do this we don't want to do this and I've actually talked in the past in really two videos why you don't want to do this I'm going to link it in the description Down Below in that video I do explain result as well but here we're going to focus just on result not so much on the exception side of things now there's something very interesting I want to bring to your attention even though that result pipe doesn't natively exist in the BCL in the.net dot next project on GitHub where many of the things that might be coming are actually trialed we did have a result implementation which was pretty insane I mean I can make this a bit smaller but there's quite a lot of stuff in here it's almost 657 lines of code and it's a very interesting implementation of that type however I'm not going to be using that because it's a bit too complicated for what we want to explain here and what I want to show what I'm going to use instead is a library called language extensions and the reason why I'm using that is because it has a very solid implementation of result and many other functional Concepts which I'd really like to talk about if you want to talk about them things like optional for example or some or none or unit then leave a comment down below so now we have that nuget package installed and the reason why I brought up that Microsoft thing is because there is sort of not really a need but an interest in that concept because it can really simplify your code so with that now installed what I'm going to do is change this code namingly the create in the update endpoint to use that result type the first thing I want to change is my interface over here so create over here won't return movie but it will return result of moving and as you can see there is no second type here you only have one generic T-Type parameter in this implementation of result we're going to explain why in a second but now we have result here and in update we have resolved nullable and it's notable because update needs to update something and if that something doesn't exist then movie will be null with those two things now in place I'm gonna go ahead and just update the update and the create endpoint over here and what I'm going to do is change how we deal with this flow the problem I have with this service and the way this code is written is that this service could be in the application layer or even deeper and it will be really hard to know that the way this is designed is to throw an exception that eventually you have to catch and if you want to use the same application layer in a CLI application it doesn't really have the same middleware functionality it becomes very very tricky to be consistent with the logic the control flow is managed by exceptions and it really shouldn't because it makes it very hard to follow maybe in the context of an API it is fine but the moment you want to start reusing stuff this just falls apart and it's very hard to make portable so what I'm going to do instead is use the validate async method and get a validation result box I'm going to say validation result and now have the result this way I'm not getting an exception and now what I can do is say that if validation result is invalid so if it is not valid then I'm gonna create the error which in this case is going to be the same validation exception that we would have thrown previously and I'm gonna pass down the errors in the Constructor because that's what that validation exception actually expects so that's absolutely fine same thing but we do not throw the exception we do not throw that error what we're going to do instead is we're going to return it so we're going to say return new result and pass the error here so now if we take a look at the Constructor or one of the constructors of this result type it has one for exceptions the exception is not thrown it is passed in as this error value and if you've used any other language that has a result type this is all too familiar so now we've done this here I'm gonna go ahead and do it again in update so same thing here as before I'm gonna go ahead catch this and return it this would be now the movie and that is it everything now works but this is only half the story because yes now we don't throw those exceptions we just return them as simply types but we need to do something with them and this is where it becomes very interesting because like we said before this result type can only be one of the two values either the happy path or the unhappy path and if I say result dot I can't say give me success or give me failure I can say things like map match I can say if fail do something if success do something and so on so what we want to do here is actually match it or we want to match the happy path the successful path and the failure or the error path so both of them will actually return the same time the type will be the I action result because that's what my API is expecting but we're gonna do it in two different ways we're gonna have the movie path the happy path the successful path here and then we're gonna have the error the bad path over here the successful path is just hey just go ahead and create the movie and that is it so we can reuse some of that we're gonna get M here and use it here and here and now the code just compiles and then for the error I just want to replicate the logic I had in the middleware which is just hey return this bad request validation sort of error so all I want to say here is error dot map to response that will map it to the appropriate contract response type and I want to wrap all that to a bad request so now I have the exact same functionality and in fact I'm gonna just take a breakpoint here just quickly also rewrite this one over here to return the appropriate types so update is also now Rewritten to use the exact same logic and I'm gonna go ahead and just debug this code base and I'm gonna go to postman and find that same request and see what we get so request comes in we have the movie we go into the service we're gonna see that validation actually does fail we're gonna create that error object with all the errors in this case only one we're gonna go and return it and then we're going to match and map it because we are forced to capture and we're forced to deal with every single scenario of that result type and then we get the result back saying hey year of release must be equal or less than 2023 and that is it so now the code is simpler the consuming side all we have is this result type over here we're matching that we say okay we have a good path and a bad path we are forced to deal with both of them and then we return appropriate types and responses for both of them and even in the service you're gonna see exactly what's going on and what's being returned now you might not like using the exception class to represent an error state but this will actually make it very easy if you want to migrate from an exception focused sort of flow control to a result based flow control all so I understand why the decision was made to make this an option if you don't want to use that then what you can use instead is a library like fluent result for example where if I scroll down it's a nuget packet with 3 million downloads or so it's a very popular library and as you're gonna see here there's many options to deal with your own error States we have error list of Errors there's some really nice goodies here it's a nice Library I'm gonna put a link in the description if you want to check it out but if you already have exceptions and you want to migrate this approach with language extensions will make it easier for you I think now when I take a look at the implementation of result just to see what's happening behind the scenes so like the one I wrote before this is a read-only struct this is done to minimize allocations as much as possible but this one also implements two interface is the I comparable and the I Equitable so you can compare and check equality on different result types and then you have the state represented with a faulted or success flag you have the a value which is the happy path and then you have the exception or the error and then you have your two Constructors for this Union type where you have the success Constructor where things are set the value is set an exception is null and the failure or the faulted state where you have the state folded in an exception with this and then the default value for the value then you have the exact same implicit operators as in the type that I showed you but you also have this pure attribute which is used to signify that this method this operator these things over here which as you can see this pure method exists in basically all of them they're marked as pure because they do not mutate the state of anything really they just are consistently going to return the same value over and over again and then you have things like map acing where you can actually map that state but still handle both of them and you have things like other operators like bigger than smaller than smaller than equal and so on and then that compared to as well from that comparable interface so it's a way more complete implementation but the concept is still the same you have this Union that can only be one thing at a time and we represent it that way now if you don't have any sort of functional exposure at all it can suddenly look a bit weird to begin with but the more you use it and the more you're forced to handle those scenarios you're gonna start it actually forces you to write better code and more predictable code so I personally quite like this approach but now I don't know from you have you used the result type and what do you think about it is it something you would use and replace especially exceptional throwing with it or not leave a comment down below and let me know well that's all I have for you for this video thank you very much for watching special thanks to my patreon so making videos possible if you want to support us you're gonna find the link in description down below leave a like if you like this video subscribe like this in the Bell as well and I'll see you in the next video keep coding
Info
Channel: Nick Chapsas
Views: 73,074
Rating: undefined out of 5
Keywords: Elfocrash, elfo, coding, .netcore, dot net, core, C#, how to code, tutorial, development, software engineering, microsoft, microsoft mvp, .net core, nick chapsas, chapsas, dotnet, .net, result type, result, result type .net, result type c#, result vs exceptions, replace exceptions with result, What’s the Result Type Everyone Is Using in .NET?
Id: YbuSuSpzee4
Channel Id: undefined
Length: 14min 46sec (886 seconds)
Published: Mon May 22 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.