Fluent Validation in C# - The Powerful Yet Easy Data Validation Tool

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
data validation is extremely important the problem is they can create a lot of excess code plus you end up in the position of having similar validation code throughout your application violating the DRI principle in this video I'm gonna introduce you to fluent validation a free tool to help make your validation clean easy to create and easy to maintain as a bonus it even works on models that you don't own and can't change now if you're new to this channel my name is Tim quarry and it's my goal to make learning c-sharp easier whether you're new c-sharp or have used it for years you should be able to find something here that will benefit you one of the biggest ways you can see improvement in your c-sharp skill is through practice make sure that whatever you learn you practice that's why I have an ongoing series on Thursdays called the weekly challenge I would encourage you to participate each week to make sure you keep your skills sharp ok let's get started now as you can see we're starting out with some code already written if you want to have a copy of this starter code or if you want to get the code from the end of this video file the link in the description below to go to the blog post entry for this video now I've got a forum project here dashboard for me create a user first name last name account balance and date of birth now it's not much here but behind the scenes let's just look real quick just to show you what code there is we have a binding list that binds to our errors list which wasn't even wired up yet it's just a basically a list of string it's not even wired yeah to our error list box then we have this create button click and again there's code behind here and as well I've mentioned in previous demos and I mention here as well this is demo code so it's be clear here I wouldn't normally put all this code in this button click event I usually have it if I could in a different layer of my application or if I was gonna pull this information out I would have it at least in a different method but there it is just this is demo code so what does it clears the errors out that's that up here and then it has a tryparse on the account balance and once it's done that as long as it works if it does not work it puts an error there and says hey that's a bad balance and returns but if it if it's works then it creates a new person model which is this person right down here this model which has first-name lastname account balance and date of birth which sounds familiar so we put our first-name text in the first name last name in a last name account balance into account balance and date of birth into day of birth so pretty simple stuff but we're getting information from the user in one of my very foundational tenets of programming is never trust the user and that's not to say that we're saying users are bad but users will find a way usually accidentally to give you data you never anticipated well my common examples is you ask a person for their age and you expect them to give you a number as in 1 2 3 4 but in numeric form and they give you te n well that's not a number that's a string but they don't know that that's a number to them so you wouldn't actually think of that so when we get information from the user we shouldn't put it straight into the database so that's kind of we're doing here I'm just simulating that I'm not gonna actually going to do it but this would be a step to insert this in the database so what I do here is somewhere up in here I would validate my data ok and validation in case you're not familiar just means I need to make sure that it's as much as I can that it's valid data as in real data so for example a person's first name needs to have something in it same as last name and account balance we really can't check that unless we have some rules around it so for example as long as it's a number is that invalid like the account balance could be a negative made a balance to check or something like that in which case it's a negative where they owes something and zero is a valid balance because may they have no money coming in or going out that's not zero account balance so that might not be something can check unless we have some rules around it for example maybe this is a particular kind of checking account where you have to have a balance of at least $5,000 in order to maintain this type that back to me something we check date of birth well a day of birth it probably can't be in the future and I know it came to the Future and it probably won't be something that is more than let's say a hundred and twenty years ago because that's older than the oldest person on the planet so that's a range we can put as date of birth sort of validate these things we could put these checks in a place the ala Sud we're starting to write this complex code or at least a number of checks so if we put the checks right here we recheck as soon that comes in the first name check to make sure it's a valid name it's it's not null it's not white space and it's it got at least one character in it same and last name account balance may well say that yes the rule is at least a thousand dollars in the account so we could check for that as well and date of birth we're gonna check that it's not greater than today and it's not over the one hundred and twenty years ago so those are checked be put in place and if we put them right here that would make sense for this particular form but what if we had a different form that also gathered information about the the user well then we've got to do validation there as well and so then we think about well maybe you could put it in the model itself and sometimes that works and you can even do decorators which those are nice and I've done a video on those but maybe you don't control this model made of class library is actually a dll and you don't have access to make modifications to it or maybe you just don't want to pass all that data around all that those methods around with it because you want to use other places or in different ways or you want to swap out which ones you use well then you gotta create different place for all this and so it kind of becomes a little bit of a depends on your circumstances type of answer for validation well today we're gonna look at fluent validation like I said now what I'm gonna do is I'm going to put it in the front end now you don't have to do this in fact it might not be the right place for you might be better to put it in the library for you with your of it with your models but in this case I'm gonna put it in the front end because the fact that I want to simulate the idea of I have no control over this library so I'm not gonna change anything in this library so I'm gonna do is I'm gonna add a reference in nougat so manage new get packages in my dashboard UI I'm gonna search for fluent validation there we go and the red asterisk is the one you want it's been downloaded 9.3 million times so it's a very popular library it has actually got a lot of add-ons as well but we're gonna use just this fluent validation for it today so install that for me it's version 8.1 point oh and notice down here it will work with dotnet standard dotnet framework and other versions of a standard M framework which also means orcid dotnet core so that's a a really great things we can we can take this to any either asp.net or Windows forms or console application to UPF or even you know xamarin iOS those type of things we can take fluent validation with us for those things so this is a truly cross-platform tool which I try to make all my tools cross-platform because of the fact that yeah you might need to go that direction someday so let's just check real quick and see annotations we can upgrade if you want to I'm gonna leave that as is we don't need to do that that's all we need to do to prepare for annotations or for this fluent validation so I'm gonna right click on my UI and create a folder for the the validators I'm actually gonna call it the validators and in here let's create a person validator which is a class person validator and I get public and I am going to inherit from abstract validator control dot to bring in the using fluent validation and this is going to validate the person model okay so I specify that this person validator is going to validate the person model now notice this is in the UI project and yeah I'm validating something somewhere else in this case somewhere I'm simulating I have no control over you could put this in the same library and be just fine so this right here will be the tool that we use to validate the person model which means that all the code for validation will be in one spot and it will be attached to the person model without being tied to it meaning the person model can move around we can have different validators also for a person model so you can kind of swap them out and yet everybody has accesses value to use it if we want to so a value is pretty simple let's create a constructor that's where the value kind of lives let's create our first rule we say rule four and we do our open parens and we give it a name it's gonna use a an arrow function or a lambda function so we're gonna choose what our variable name is let's call it P for person and we'll do our arrow function and say P dot first name so I'd say okay we're gonna work on the first name property so it's getting a rule for the first name we say dot and here's all the potential rules we can do in other actions and there's a lot in here but let's start with something simple not empty now what is not empty if we look at this list here it says defines a not empty validator that's great it will fail if the property is null is empty or is the default value for the type so that covers quite a few different options for our first name now let's leave it just like that with nothing else we created one rule that's it save that and now let's come over to our dashboard here we're going to validate our code and the validation I'll put it all in one spot so you can see it so person validator which we have the ABI using statement for let's call it validate or equals new person validator and they can say let's do I'll show you the results but let's just do a var for now results equals validator dot validate person so we're passing in our newly-created person object we're saying go ahead and validate this and the results that come back is called a fluent validation dot result that validation result okay so that's that's what came back we could say validation result like so and then do a control dot and add this second using statement fluent validation doubt results but if you wanted to do var that's okay as well it's kind of up to you I usually shy away from var for simple types but when it's complex like this and it's obvious what it is for then I I sometimes use var it's up to you okay so we've we've just said go ahead and validate this person so what's going to do when we create this person validator it sets up these rules now you notice there is no validate method in my validator that's because in the abstract validator okay there's a whole bunch of stuff in here about setting up and there's validate so it passes back of validation results and that's a you know kind of the information about it so what's gonna do is we write we call a validate method either gonna say is this a valid method or model based upon the rules you specified we only have one rule and the rule is that first name can't be empty so let's go ahead and create the if statement here if results dot is valid equals false okay so results dot is valid that's pretty self-explanatory if it's true that it is valid if it's false it's not valid it's gonna check for false here then I'll do is I'll do a for and will say I believe a string failure in results dot errors so errors actually no it's a validation failure there we go so now we have a list of errors we can look through if this model is not valid so let's for each validation error let's add it to our errors list okay so we have our errors dot add we ask string so let's create our string interpolation and we'll start off with just the name so failure dot property name and then but the format use up here was this is the name and then colon and then the the error so let's say colon and the error is fail your dot error message okay so that's it what's going to happen is if we fail the validation it's going to loop through every error we get back because we have more than one error and if we get back more one error that's gonna mean there's two entries here are three entries or five entries and it's going to loop through each one one or more and add that to our errors list so let's go wire this errors list up so our list error list box dot data source equals errors and that should do it we can verify that by putting in a couple of bad values or just one bad value actually we know the account balance if we put test here we hit create it's an invalid amount so let's put a zero there and let's put nothing for a first name either the operation is complete but first name must not be empty now where'd that message come from well that came from our validation because it failed validation so if I put test here operation is complete no errors in the list because test could be a valid name so that's really all there is the validation is pretty simple stuff but here's the cool thing is we're not done here we can chain more things to this rule for example okay the first name is not empty but what if it's a hundred characters that doesn't seem like it's a valid first name either and probably not one character that's an initial it's not a name I think the first name has to add at least two characters so if we said that's the rule I'm not sure if it would be or not in my case in the real world I'd probably say as long as one character more call it good but probably last name last name probably to be two characters or more but in this case let's just do two characters or more so let's just say the length and this is the cool thing okay that's provided but we have this exact length here but maybe you don't want that maybe instead we want is we want a min a max that sounds better so let's say that minimum is two characters and maximum is 50 characters since our database table only accepts up to 50 characters that's a good validation rule here if we stop the user and say nope if your name is more than 50 characters then it's gonna have a problem a database so let's tell you right up front and maybe you can use a nickname or maybe you can rethink the name you were putting in because maybe you were putting in three names your first and middle names or something like that it's an a length of to 250 now that error message wasn't the greatest so what if we said with message and said please provide a valid first name like so that's you know more stuff attached to that first name rule but now we're kind of dialing in the rules his first name let's write again first name create oh I've got an invalid account balance first name first name must not be empty and please provide a valid first name okay so what happened here well it ran both rules and the second one has a message no problem here you're going to do let's we can use our Enter key this is kind of nice the Enter key allows us to put multiple lines kind of shrink down the width this line so it's not so long and yet at the same time it's still one line as far as the compiler is concerned so we can do now is we can actually put another message here I just copy this and paste it but I'm starting to get long names let's change you to something shorter so not empty is let's say the the first name is empty likes that and for this one is length of first name invalid okay it means not quite as short I hope it to be but that's get is there and let's run that real quick and see it working so first name where I leave blank will put a value in for our account balance and hit create now it says the first name is empty as it's a length of first name is invalid so we're getting both of those messages which is we now have both custom messages but what if he didn't want both messages because why tell them this empty and the length is invalid so we can do is we add one more property at the beginning let's say cascade and this is a rule essentially against the rule so here's how you're gonna process cascading so cascade mode let's say that it is stopped on first failure no notice you say dot this and then dot this then dot this just keep adding and extending this rule so now we're going to stop in the first failure that's just for this particular rule so we're if if this rule the not empty rule fails it's not going to check the length let's validate that or verify that so my account balance is 1 now we have one error first name was empty well let's put the T in there now the first name is not empty but the length of first name is invalid so let's put another check in place for this string because it is a string and if I were to put one two three they go oh no I'm a account balance but never first name that says it's a valid name there's no error here say that's an invalid name the 1 2 3 is not a valid name and that's where you might have to deviate outside of the standard built-in rules they're provided for you that's not a problem we're going to create a protected method so protected bool be a valid name say string name so this is gonna sound a little weird be a valid name but when you put it in the context of our rule it'll make sense and it's gonna read very easily because so far we read very easily rule 4 first named cascade the stop on first failure if it's not empty with message a length with message to kind of reads almost like just normal English yeah it's a little stilted it's got a couple of quirks but it's very easy to understand we want the same thing for our method names so it's a custom rule we're gonna create in for this rule we're gonna do is we're a strip out a couple of the characters that are ok to be a name but they're not alphanumeric alpha characters and by alpha I mean ABC but beyond that there's also other characters that may be in there you know with accent marks thing like that we're going to check for all of those but first we'll strip out a couple books throughout space and we'll strip out the - because those could be a name so we're gonna say name equals name dot replace and we'll replace a space and it has to be double quotes here not sale quotes we replaced that with an empty string so it's replacing a space with an empty string and we'll also replace the a dash with an empty string now typically the replace we do a char which is a single quote but since we've outputs an empty string in place we have to use double quotes it's a quirk okay so we don't really care for this name so that's why we're modifying it we're just checking to make sure that it has only valid characters so I've stripped out the two that might otherwise cause a problem with this return name dot all it's got check every character in this name I'll say char dot is letter in the is letter we'll check for any valid letter and that's Unicode letter so it does do the accent characters and other things that aren't in a u.s. standard keyboard okay so this allows it to go a little more international with our our wording which probably might affect you it probably should because usually a name database is going to be international so with this what's gonna do is return name dot all and what all does is says dude does every character in this sequence pass the it's a letter test so go through every character and check to make sure it's a letter that's why I pulled the spaced out first and the dash out first because those are not letters but they are valid for a name so if all of them are letters now you've removed the two non letters that are okay that's gonna return true otherwise they'll return false let's add this test at the end so must be a be a valid H okay now I can add I'm sorry a valid name about H be a valid name and we're going to add the with message and we'll say that name will say first name contains invalid characters like so now notice I'm typing first name a lot so we're gonna get back to that in just a minute verify this works first so let's get back here 1 2 3 1 2 3 create first name contains invalid characters so now it's failing but if I were to say te s T is a valid name so there we go now we have a name check it's a little more granular in its check but we're still like I said using this first name over and over again which is especially problematic because once I get this rule done I think this is pretty good once I get done I'm gonna copy the whole thing and paste it for lasting well let's use one of the built-in variables that the fluent validation provides us and say property name so we replace that everywhere is just in curly braces and it's writing a string no dollar sign up front or string that format necessary and the really cool thing here that with the property name is when it looks at a property name and sees first name it the F in the anti capitalize it says ok those are two different words therefore I'll split it apart and put a space in between let's see it's run again and I'm going to say I leave it empty create first name is empty now let's put something in there length of first name invalid t2 first end contains invalid characters so now it's telling me that the field name in my string which means I don't need to have this the beginning probably in fact let's get rid of it he'll save us some space and let's just actually say we'll just add just the error message like so and now if we run this let me do this it just has the error message and since it says first name we know which property applies to so we can use these variables to tell us information about what we're checking against but also we could say this length right here well how many characters was it so what if it was more than enough character me is 51 characters we could take one off but if it's a hundred and one characters well that's a little harder to deal with we have to do something different telling the user that would allow them to make a choice so let's put it inside parens rafter length and just say just a number so we'll put inside curly braces total length and this one he works for string fields but it's kind of handy total length so now if you run this again and I put one here and let's first of all put just one letter in length of length one of first name invalid that's okay but what if I just start typing letters crazy hit create a hundred and five so now if I take off well let's say I have half of G's or more than half there you go all the G's I'm done I 73 I'm making progress okay so you can very easily see Oh only six away one two three four five six there we go but if I put K in there I'm sorry one over so that allows me to know exactly where I'm at in my in the validation it's you know just a little outside or it's way outside so more information can provide the user the easier it makes their job in fixing the errors so as there's a number of different variables for example the min length and max length to get put in there as well so to grab this number right here instead of you have to type it out again the property name the property value as well so you can you can grab these different parameters and put them into your string directly with the curly braces around them and create some really nice error messages for your users all right so we've done this for one field the first name let's do it for last name copy this whole thing and paste and yet sounds redundant but we may be treating last name differently so create a similar rule set is okay and notice since we use property name everywhere I didn't have to redo these strings now also notice that the with message we also have with localized message so if you are dealing with an application needs to have localization this is a way to use a localized message and adjust for whatever language you're displaying your information in so it's not just in this case just English you can have Spanish or French or whatever by using the localized string I'll cover that in this video but just know that's there because yes these string messages are a little scary in the fact that they're just one language alright so let's just make sure this this runs so I'm gonna say now notice that the Cascade mode is stopped on first failure so I'll just puts a 1 account balance and create notice that first name is empty and last name is empty so just no cascade mode only applies the rule it's in so even though we stopped after this right here that was for this rule and we've stopped after this one for this rule so just note that even if you do a cascade mode for your entire your entire validator what you can do a global for this whole validator it still is per rule it's not like if the first rule stops no other rule gets run all right and part of the reason for that is because this valuator can run asynchronously if you want it to and so it run these rules in parallel so that's why you can't say if one fails the others stop because then you get inconsistent results as to which one stops everything else now there is a way and then I cover it in this video but the documentation that site is great so you can go and see how to do it there is a way to nest rules so that if you do the first check and it fails then you don't do other checks on other properties even so it's ways of doing that but in general we just stack the rules like this and then they'll get run in parallel if you're doing async wrestling all right last rule just kind of show something a little bit different so let's create a rule for and rest say P and the arrow function P dot date of birth and we're gonna say that this one rockin we're about cascading for this but we'll say must be a valid age okay now I don't have that yeah I'm going to create that and we'll do a whiff message and we'll say invalid and we'll put the the proper name which we date of birth now yes is yell at me so go ahead and create that's protected bool and we'll say it's going to date time we'll call it date so date you're passing it right check to make sure this is valid so the first thing I'll do is I'll grab the year of this year so it'd be 2018 so int year let's call it current year equals date time dot now dot year let's get 2018 they're going to check to see if the year on the date is is valid so actually grab that year as well we'll say DOB year date of birth year equals date dot year so now we have these two years to compare and all I make sure is that the date of birth year isn't in the future so it's not 2019 or beyond and I want to check as well is that the day of birth year is not more than a hundred and twenty years ago so if current year let's actually say did birth year date of birth year is less than or equal to the current year if that's true then that's good and the date of birth here is greater than the current year - 120 okay so there's our two different checks if it's less than or equal to this year and if it's greater than a hundred and twenty years ago if both of those are true return true okay come down here and return false I could put that in else but the reality is this return stops it right here so it only gets here if it was false so it's up to you if you want to do that or not the other thing I could have done is I could have just returned this right here I don't find that very easy to read because it kind of make you think a little bit what's going on whereas with this I can break point I can look through it and it's just a little easier and it's a little more understandable so I don't typically do that but remember any if statement the result is a true or false value so in theory you can return that value and I'd be true or false and that's we're looking for it so now you have this be a valid age so date of birth must be a valid age with message invalid date of birth let's try it so let's test and test and one two three now a date let's leave it as correct and that's that passes let's move it into January 1st of 2019 invalid date of birth let's move it back to oh I don't know actually probably changing the picker easier let's let's do this let's say 1934 that's fine but now what if we did the 1800s 1866 that's an invalid date of birth okay so that gives you a little more information about you know kind of bracketing in something or you know in other ways of kind of limiting the data that comes through and making sure that's valid data so we have three different rules or three different rule sets one for first name one for last name those are similar basically the same and one for date of birth so all you to do to add more validation is just add more rules into this constructor that's it if you want to create more custom rules no problem just create these little bits right here and it's just pass in the value pass back a boolean as to whether or not it's valid so you don't have to put these in your in your validator you can put them outside of that and have this for your entire application to share so you create these anywhere in public instead it protected probably but what does it do is allow you to you know make name validators that can use anywhere in any model validation not just in this specific one you can even make a company-wide one or create something new get package where you can create your own standard validators that go with the standard values that come with the fluent validation okay so don't think you're limited to putting it inside of this particular validator you can bring them outside now there's a lot more they could cover I'm not going to they want to get you started in this so you know get in get your hands dirty try it out make your own validators try some different data types may validate that account balance or other things as well and just try and figure it all out because if you don't use it then you don't really grasp it as a sale URI dope doesn't click as well okay so try it out and then once you get it then unless you do is I want you to go to the website and read the documentation because there's so much more that you could learn about and the documentation is excellent so go this website fluent validation dotnet and there'll be information there that will cover some of the more detailed aspects of fluent validation okay things like if you have models inside your models or lists of models those type of things so definitely check it out once you gotta start to use this and it'll probably come up in one of my future weekly challenges we've seen like something you should try out because validation is very important so figure out a way to make your validation easy and yet powerful and yet also the ability you have the ability to be dry doing it and I think this one flue and validation fits all those criteria and it's just great so try it out and let me know you think down the comments below alright I appreciate thanks for watching and as always I am Tim quarry [Music] you [Music]
Info
Channel: IAmTimCorey
Views: 78,928
Rating: 4.948936 out of 5
Keywords: .net, C#, Visual Studio, code, programming, tutorial, training, how to, tim corey, C# training, C# tutorial, fluent validation, c# fluent validation, fluent data validation, data validation, c# data validation
Id: Zh1ccvTFzs8
Channel Id: undefined
Length: 43min 58sec (2638 seconds)
Published: Mon Dec 10 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.