{"id":22065553,"url":"https://github.com/karenpayneoregon/classvalidationvisualbasic","last_synced_at":"2025-03-23T18:16:54.582Z","repository":{"id":110840358,"uuid":"191004020","full_name":"karenpayneoregon/ClassValidationVisualBasic","owner":"karenpayneoregon","description":"DataAnnotations for .net framework, .net core framework for C# and VB.NET","archived":false,"fork":false,"pushed_at":"2025-01-15T12:23:12.000Z","size":4543,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"net-core-version","last_synced_at":"2025-01-29T00:28:46.953Z","etag":null,"topics":["csharp","dataannotations","vb-net","vbnet","windows-forms"],"latest_commit_sha":null,"homepage":"","language":"SCSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karenpayneoregon.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-09T12:31:36.000Z","updated_at":"2025-01-15T12:23:14.000Z","dependencies_parsed_at":"2025-01-15T13:47:26.125Z","dependency_job_id":"39d23ad3-d550-4c0e-9291-5b0a43b618b0","html_url":"https://github.com/karenpayneoregon/ClassValidationVisualBasic","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2FClassValidationVisualBasic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2FClassValidationVisualBasic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2FClassValidationVisualBasic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2FClassValidationVisualBasic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karenpayneoregon","download_url":"https://codeload.github.com/karenpayneoregon/ClassValidationVisualBasic/tar.gz/refs/heads/net-core-version","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245144974,"owners_count":20568056,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dataannotations","vb-net","vbnet","windows-forms"],"created_at":"2024-11-30T19:19:29.282Z","updated_at":"2025-03-23T18:16:54.539Z","avatar_url":"https://github.com/karenpayneoregon.png","language":"SCSS","readme":"### Class/model validation basics for web and desktop\n\n![Learn With Karen Validate](assets/LearnWithKarenValidate.png)\n\nThis repository provides various code samples for validating data using [data annotations](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=net-5.0) and [FluentValidation](https://docs.fluentvalidation.net/en/latest/installation.html).\n\n# Validating application data\n\nValidation is the first and most important step in securing an application. It prevents the application from processing unwanted inputs that may produce unpredictable results. Couple validation with properly transmitting data to a data source.\n\nWhen validating data should there be instant feedback? This is subjective, instant feedback will be better when there are many inputs so this would be better than waiting to submit their input. In the case of instant feedback there needs to be events triggered to perform validation while on submit there is a central generic method to perform validation.\n\nLet's take another view of validating on submit, with all types of application which can be created with Visual Studio there are methods which permit displaying messages to assist the user to correct mistakes. So back to subjective, it may come down to if code is written in a team or individual in regards to, are there team rules or it's the Wild West.\n\n## Preface\n\nThis repository contains validation code samples for user input, not validation of data coming from a database or other external sources, for working with databases and external sources that deserves it's own article.\n\n\n## Data annotations\n\nWith ASP.NET Core using [data annotations](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=net-5.0) the standard for validating a model is with the [Validator Class](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validator?view=net-5.0) which defines a helper class that can be used to validate objects, properties, and methods when it is included in their associated [ValidationAttribute](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationattribute?view=net-5.0) attributes.\n\n\nThe ValidationAttribute class enforces validation, based on the [metadata](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.metadatatypeattribute?view=net-5.0) that is associated with the data table. You can override this class to create custom validation attributes.\n\n**Example**\n\n```csharp\npublic class Person\n{\n    public int Id { get; set; }\n    [RegularExpression(\"^.{3,}$\", ErrorMessage = \"{0} Minimum 3 characters required\")]\n    [Required(ErrorMessage = \"{0} Required\")]\n    [StringLength(30, MinimumLength = 3, ErrorMessage = \"Invalid {0}\")]\n    public string FirstName { get; set; }\n\n    [RegularExpression(\"^.{3,}$\", ErrorMessage = \"{0} Minimum 3 characters required\")]\n    [Required(ErrorMessage = \"{0} Required\")]\n    [StringLength(30, MinimumLength = 3, ErrorMessage = \"Invalid {0}\")]\n    public string LastName { get; set; }\n\n    [ValidateYears(ErrorMessage = \"Valid range for BirthDate is from {0} to {1}\")]\n    [Required(ErrorMessage = \"{0} Required\")]\n    public DateTime BirthDate { get; set; }\n}\n```\n| Property        |   Attribute    |   Description |\n|:------------- |:-------------|:-------------|\n| FirstName | RegularExpression | Minimum 3 characters required |\n|       | Required | Must have a value couple with above, minimum of three characters |\n|       | StringLength | Min of three characters, max of 30 characters |\n| LastName | RegularExpression | Minimum 3 characters required |\n|       | Required | Must have a value couple with above, minimum of three characters |\n|       | StringLength | Min of three characters, max of 30 characters |\n| BirthDate | ValidateYears | Custom attribute for range of years |\n\nTo validate an instance of `Person`, set up a unit test method where all properties are valid.\n\n```csharp\n[TestMethod]\n[TestTraits(Trait.Annotations)]\npublic void ValidPerson()\n{\n\n    // arrange\n    Person person = new ()\n    {\n        FirstName = \"Mike\",\n        LastName = \"Flowers\",\n        BirthDate = new DateTime(1932, Now.Month, Now.Day)\n    };\n\n    // act\n    EntityValidationResult result = Model.Validate(person);\n\n    // assert\n    Check.That(result.IsValid).IsTrue();\n}\n```\n\nNext setup another test method, in this case BirthDate is out of range.\n\n```csharp\n[TestMethod]\n[TestTraits(Trait.Annotations)]\npublic void InvalidDateValidPerson()\n{\n    // arrange\n    DateTime date = new (2022, 4, 27);\n\n    Person person = new ()\n    {\n        FirstName = \"Mike\",\n        LastName = \"Flowers\",\n        BirthDate = new DateTime(1931, date.Month, date.Day)\n    };\n\n    // act\n    EntityValidationResult result = Model.Validate(person);\n\n    // assert\n    Check.That(result.IsValid).IsFalse();\n\n}\n```\n\nIn both test, the following code validates an instance of `person`\n\n```csharp\nEntityValidationResult result = Model.Validate(person);\n```\n\nFollowed by asking if the instance of `person` is valid by `IsValid` method. Check is from [NFluent](https://www.n-fluent.net/).\n\n```csharp\nCheck.That(result.IsValid).IsFalse();\n```\n\n- All code to validate are in a class project in this solution under [BaseDataValidatorLibrary](https://github.com/karenpayneoregon/ClassValidationVisualBasic/tree/net-core-version/BaseDataValidatorLibrary).\n- For a list of stock validation attributes see [System.ComponentModel.DataAnnotations Namespace](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=net-5.0).\n\n## WPF\n\nThere is a simple project taken from the [following repository](https://github.com/karenpayneoregon/wpf-login-annotations-cs.git). In the project NetCoreUnitTestProject there are unit test against the model in this project.\n\n![image](assets/Login2.png)\n\nhttps://github.com/karenpayneoregon/wpf-login-annotations-cs.git\n\n## ASP.NET Core Razor\n\n*Some inputs use default validation while others have custom rules.*\n\n![Create Movie](ValidationSample/assets/createMovie.png)\n\nIn markup, the following uses `asp-validation-for` tag helper, see [Validation Tag Helpers](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-6.0#the-validation-tag-helpers) documentation for more information on usage..\n\n```asp\n\u003cdiv class=\"row\"\u003e\n    \u003cdiv class=\"col-md-4\"\u003e\n        \u003cform method=\"post\"\u003e\n            \u003cdiv asp-validation-summary=\"ModelOnly\" class=\"text-danger\"\u003e\u003c/div\u003e\n\n            \u003cdiv class=\"form-group\"\u003e\n                \u003clabel asp-for=\"Movie.Title\" class=\"control-label\"\u003e\u003c/label\u003e\n                \u003cinput asp-for=\"Movie.Title\" class=\"form-control\" /\u003e\n                \u003cspan asp-validation-for=\"Movie.Title\" class=\"text-danger\"\u003e\u003c/span\u003e\n            \u003c/div\u003e\n\n            \u003c!-- \u003csnippet_ReleaseDate\u003e --\u003e\n            \u003cdiv class=\"form-group\"\u003e\n                \u003clabel asp-for=\"Movie.ReleaseDate\" class=\"control-label\"\u003e\u003c/label\u003e\n                \u003cinput asp-for=\"Movie.ReleaseDate\" class=\"form-control\" /\u003e\n                \u003cspan asp-validation-for=\"Movie.ReleaseDate\" class=\"text-danger\"\u003e\u003c/span\u003e\n            \u003c/div\u003e\n            \u003c!-- \u003c/snippet_ReleaseDate\u003e --\u003e\n\n            \u003cdiv class=\"form-group\"\u003e\n                \u003clabel asp-for=\"Movie.Description\" class=\"control-label\"\u003e\u003c/label\u003e\n                \u003cinput asp-for=\"Movie.Description\" class=\"form-control\" /\u003e\n                \u003cspan asp-validation-for=\"Movie.Description\" class=\"text-danger\"\u003e\u003c/span\u003e\n            \u003c/div\u003e\n\n            \u003cdiv class=\"form-group\"\u003e\n                \u003clabel asp-for=\"Movie.Price\" class=\"control-label\"\u003e\u003c/label\u003e\n                \u003cinput asp-for=\"Movie.Price\" class=\"form-control\" /\u003e\n                \u003cspan asp-validation-for=\"Movie.Price\" class=\"text-danger\"\u003e\u003c/span\u003e\n            \u003c/div\u003e\n\n            \u003cdiv class=\"form-group\"\u003e\n                \u003clabel asp-for=\"Movie.Genre\" class=\"control-label\"\u003e\u003c/label\u003e\n                \u003cselect asp-for=\"Movie.Genre\" asp-items=\"@(Html.GetEnumSelectList\u003cGenre\u003e())\" class=\"form-control\"\u003e\u003c/select\u003e\n                \u003cspan asp-validation-for=\"Movie.Genre\" class=\"text-danger\"\u003e\u003c/span\u003e\n            \u003c/div\u003e\n\n            \u003cdiv class=\"form-group\"\u003e\n                \u003cdiv class=\"checkbox\"\u003e\n                    \u003cinput asp-for=\"Movie.PreOrder\" /\u003e\n                    \u003clabel asp-for=\"Movie.PreOrder\"\u003e\u003c/label\u003e\n                \u003c/div\u003e\n            \u003c/div\u003e\n\n            \u003cdiv class=\"form-group\"\u003e\n                \u003cinput type=\"submit\" value=\"Create\" class=\"btn btn-primary\" /\u003e\n            \u003c/div\u003e\n        \u003c/form\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\nIn code behind the model `Movies` is setup as follows [BindProperty](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.bindpropertyattribute?view=aspnetcore-5.0).\n\n**BindPropertyAttribute**\nAn attribute that can specify a model name or type of IModelBinder to use for binding the associated property.\n```csharp\n[BindProperty]\npublic Movie Movie { get; set; }\n```\n\nIn the Post action, [ModelState.IsValid](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-5.0) validates or invalidates properties in the model.\n\n**Model state** represents errors that come from two subsystems: model binding and model validation. Errors that originate from model binding are generally data conversion errors. \n\n\n```csharp\npublic async Task\u003cIActionResult\u003e OnPostAsync()\n{\n    if (!ModelState.IsValid)\n    {\n        return Page();\n    }\n\n    _context.Movies.Add(Movie);\n    await _context.SaveChangesAsync();\n\n    return RedirectToPage(\"./Index\");\n}\n```\n\nThat's pretty much the basics for ASP.NET Core.\n\n## Unit test examples\n\nThe following sections will demonstrate using mocked data in unit test methods.\n\nThe best method to test validating models is in unit test methods, devoid of user interfaces.\n\nSimple example, we have a Book class, wired up for\n\nFor `Book` class these are how each property is setup with attributes for validation. \n\n| Property        |   Required    |   Uses custom attribute |\n|:------------- |:-------------|:-------------|\n| Title | \u0026check; |  \u0026cross; |\n| ISBN | \u0026check; |  \u0026cross; |\n| BookCategory | \u0026check; |  \u0026check;|\n| NotesList | \u0026check; |  \u0026check;|\n\n\n`BookCategory` is an `enum`\n\n```csharp\npublic enum BookCategory\n{\n    Select = 1,\n    SpaceTravel = 2,\n    Adventure = 3,\n    Romance = 4,\n    Sports = 5,\n    Automobile = 6\n}\n```\n\nWhich uses a custom custom attribute inheritied by RequiredAttribute to validate a property enum is used.\n\n\n```csharp\npublic class RequiredEnumAttribute : RequiredAttribute\n{\n    public override bool IsValid(object sender)\n    {\n        if (sender == null)\n        {\n            return false;\n        }\n\n        var type = sender.GetType();\n        return type.IsEnum \u0026\u0026 Enum.IsDefined(type, sender); ;\n    }\n}\n```\n\n`Notes` uses the following class to validate an instance of a `Book` has at least one note.\n\n```csharp\npublic class ListHasElements : ValidationAttribute\n{\n    public override bool IsValid(object sender)\n    {\n        if (sender == null)\n        {\n            return false;\n        }\n\n        if (sender.IsList())\n        {\n            var result = ((IEnumerable)sender).Cast\u003cobject\u003e().ToList();\n            return result.Any();\n        }\n        else\n        {\n            return false;\n        }\n    }\n}\n```\nThe `Book class` should reside in a business class project while any `custom attribute classes` should reside in a common class project suitable for many projects to utilize rather than in a single project/application.\n\nFor this Visual Studio solution\n\n- All `validation methods`, extension methods and custom attribute classes are in `BaseDataValidationLibrary`\n- All `Classes/models` reside in the project `BaseModelsLibrary`\n\nLet's look at testing a Book. There is a test class BooksTest marked as a partial class, the test are in the root folder of the test project while the second part is in the Base folder.\n\n![img](assets/books.png)\n\nIn the base folder we setup a valid Book instance for use in test methods in the root folder.\n\n![i](assets/books1.png)\n\n\n**First test**\n\nValidates the model using valid data\n\n```csharp\n[TestMethod]\n[TestTraits(Trait.Annotations)]\npublic void ValidateBook_Good_Test()\n{\n    // arrange\n    Book book = TheBook;\n    \n    // act\n    EntityValidationResult validationResult = Model.Validate(book);\n\n    // assert\n    Check.That(validationResult.HasError).IsFalse();\n}\n```\n\n\n**Second test**\n\nEnsure that the model is not valid as the `Category` property is `null`.\n\n```csharp\n[TestMethod]\n[TestTraits(Trait.Annotations)]\npublic void ValidateBook_NoCategory_Test()\n{\n    // arrange\n    Book book = TheBook;\n    book.Category = null;\n    const string expected = \"Category is required\";\n\n    // act\n    EntityValidationResult result = Model.Validate(book);\n\n    // assert\n    Check.That(result.Errors.Any(validationResult =\u003e \n        validationResult.ErrorMessage!.Contains(expected)))\n        .IsTrue();\n}\n```\n\n**Third test**\n\nEnsure that the model is not valid as the `ISBN` property is `null`.\n\n```csharp\n[TestMethod]\n[TestTraits(Trait.Annotations)]\npublic void ValidateBook_NoIsbn_Test()\n{\n    // arrange\n    Book book = TheBook;\n    book.ISBN = \"\";\n    const string expected = \"ISBN is required\";\n\n    // act\n    EntityValidationResult result = Model.Validate(book);\n\n    // assert\n    Check.That(result.Errors.Any(validationResult =\u003e \n        validationResult.ErrorMessage!.Contains(expected)))\n        .IsTrue();\n\n}\n```\n\nThat is it although we do not have full coverage for all paths the idea is to give the reader an idea how to get started. Consider writing more test against the book model for practice.\n\n\n## FluentValidation\n\n[FluentValidation](https://docs.fluentvalidation.net/en/latest/installation.html) is a validation library for .NET, used for building strongly typed validation rules for business objects.\n\nFluent validations use a Fluent interface and lambda expressions to build validation rules.\n\nCustomer class\n\n```csharp\npublic class Customer : CustomerLogin\n{\n    public int Id { get; set; }\n    public string FirstName { get; set; }\n    public string LastName { get; set; }\n    public string Email { get; set; }\n    public decimal CreditLimit { get; set; }\n    public int Discount { get; set; }\n    public bool HasDiscount { get; set; }\n    public string Address { get; set; }\n    public string Postcode { get; set; }\n    public string Pin { get; set; }\n    public string SocialSecurity { get; set; }\n    public DateTime BirthDate { get; set; }\n\n    public override string ToString() =\u003e $\"{FirstName} {LastName}\";\n\n}\n```\n\nValidator for Customer class/model\n\n**Rules**\n\nSome rules are not realistic, they are here to show what is possible.\n\n- **Id** must be in the range from 1 to 10 \n- **FirstName** can not be empty and valid length is 3 to 10 characters\n- **LastName** can not be empty and valid length is 3 to 20 characters\n- **Email** must be a valid email address\n- **Discount** is only valid if `HasDiscount` property is set to true\n- **CreditLimit**` must be less than $9,999\n- **Postcode** must be in the method `HasValidPostcode` list of postal codes\n- **Pin** is converted from a string to an int and can not be greater than 8888\n- **SocialSecurity** uses over the top validation using regular expressions in a language extension method.\n- **BirthDate** can not be less than 01/01/1932\n\n```csharp\npublic class CustomerValidator : AbstractValidator\u003cCustomer\u003e\n{\n    public CustomerValidator()\n    {\n\n        RuleFor(customer =\u003e customer.Id)\n            .InclusiveBetween(1, 10);\n\n        RuleFor(customer =\u003e customer.FirstName)\n            .NotEmpty()\n            .Length(3, 10)\n            .WithMessage(\"Please specify a first name\");\n\n        RuleFor(customer =\u003e customer.LastName)\n            .NotEmpty()\n            .Length(3, 20)\n            .WithMessage(\"Please specify a last name\");\n\n        RuleFor(customer =\u003e customer.Email)\n            .EmailAddress();\n\n        RuleFor(customer =\u003e customer.Discount)\n            .NotEqual(0)\n            .When(customer =\u003e customer.HasDiscount);\n\n        RuleFor(customer =\u003e customer.CreditLimit)\n            .LessThanOrEqualTo(9999);\n\n\n        RuleFor(customer =\u003e customer.Address)\n            .MaximumLength(250);\n\n        RuleFor(customer =\u003e customer.Postcode)\n            .Must(HasValidPostcode)\n            .WithMessage(\"Please specify a valid postcode\");\n\n        Transform(from: customer =\u003e customer.Pin, to: value =\u003e \n                int.TryParse(value, out int result) ? (int?)result : null)\n            .GreaterThan(8888);\n\n        Transform(\n            from: customer =\u003e customer.SocialSecurity,\n            to: value =\u003e value.IsSSNValid()).Must(value =\u003e value);\n\n        RuleFor(customer =\u003e customer.BirthDate).GreaterThan(new DateTime(1932,1,1));\n    }\n\n    private static bool HasValidPostcode(string postcode)\n    {\n        List\u003cstring\u003e list = new() { \"97301\", \"97223\", \"97209\", \"97146\", \"97374\", \"97734\" };\n        var result = list.FirstOrDefault(item =\u003e item == postcode);\n        return result is not null;\n    }\n}\n```\n\nValidating a Customer, as in the Book example we setp a valid customer in the base class for the test class.\n\n```csharp\npublic partial class MainTest\n{\n    private CustomerValidator CustomerValidator;\n    /// \u003csummary\u003e\n    /// Perform initialization before test runs using assertion on current test name.\n    /// \u003c/summary\u003e\n    [TestInitialize]\n    public void Initialization()\n    {\n        CustomerValidator = new CustomerValidator();\n    }\n\n    /// \u003csummary\u003e\n    /// Perform any initialize for the class\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"testContext\"\u003e\u003c/param\u003e\n    [ClassInitialize()]\n    public static void ClassInitialize(TestContext testContext)\n    {\n        TestResults = new List\u003cTestContext\u003e();\n    }\n\n    public static Customer ValidCustomer =\u003e new Customer\n    {\n        Id = 1,\n        FirstName = \"Karen\",\n        LastName = \"Payne\",\n        BirthDate = new DateTime(1956,9,24),\n        Pin = \"8889\",\n        Email = \"kp@gmail.com\",\n        CreditLimit = 9999,\n        Discount = 10, // TODO\n        SocialSecurity = \"205-55-1234\",\n        HasDiscount = true,\n        Address = \"101 Microsoft Way\",\n        Postcode = \"97209\"\n    };\n}\n```\n\nThen in the main test class, unit test for customers.\n\nThere are several methods used for validation.\n\n- To check if a model is valid o rnot\n  - Check.That(result.IsValid).IsTrue();\n  - result.ShouldHaveValidationErrorFor(customer =\u003e customer.FirstName);\n  - result.ShouldHaveValidationErrorFor(customer =\u003e customer.Email);\n\n\n\n```csharp\n[TestClass]\npublic partial class MainTest : TestBase\n{\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CleanCustomerTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n\n        // act\n        var result = await CustomerValidator.ValidateAsync(thisCustomer);\n\n        // assert\n        Check.That(result.IsValid).IsTrue();\n    }\n\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CustomerNoFirstNameNoLastNameTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n        thisCustomer.FirstName = \"\";\n        thisCustomer.LastName = \"\";\n\n\n        // act\n        var result = await CustomerValidator.TestValidateAsync(thisCustomer);\n        \n        // assert\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.FirstName);\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.LastName);\n\n    }\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CustomerBadEmailTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n        thisCustomer.Email = \"karenGmail\";\n\n        // act\n        var result = await CustomerValidator.TestValidateAsync(thisCustomer);\n\n        // assert\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.Email);\n\n    }\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CustomerInvalidPostalCodeTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n        thisCustomer.Postcode = \"99999\";\n\n        // act\n        var result = await CustomerValidator.TestValidateAsync(thisCustomer);\n\n        // assert\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.Postcode);\n\n    }\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CustomerInvalidPrimaryKeyTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n        thisCustomer.Id = 0;\n\n        // act\n        var result = await CustomerValidator.TestValidateAsync(thisCustomer);\n\n        // assert\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.Id);\n\n    }\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CustomerInvalidPinTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n        thisCustomer.Pin = \"5555\";  // must be greater than 8888\n\n        // act\n        var result = await CustomerValidator.TestValidateAsync(thisCustomer);\n\n        // assert\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.Pin);\n    }\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CustomerBirthDateInvalidTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n        thisCustomer.BirthDate = new DateTime(1930, 1, 1);\n\n        // act\n        var result = await CustomerValidator.TestValidateAsync(thisCustomer);\n\n        // assert\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.BirthDate);\n    }\n\n    [TestMethod]\n    [TestTraits(Trait.FluentValidation)]\n    public async Task CustomerSocialSecurityNumberInvalidTest()\n    {\n        // arrange\n        Customer thisCustomer = ValidCustomer;\n        thisCustomer.SocialSecurity = \"219-09-9999\";\n\n        // act\n        var result = await CustomerValidator.TestValidateAsync(thisCustomer);\n\n        // assert\n        result.ShouldHaveValidationErrorFor(customer =\u003e customer.SocialSecurity);\n    }\n}\n```\n\n\n\n\n\n\n\n\n\n\n\n\n:raised_hand: **04/25/2022** version in branch [net-core-version](https://github.com/karenpayneoregon/ClassValidationVisualBasic/tree/net-core-version) includes C# .NET Core versions of original work done in .NET Framework.\n\n# VB.NET\n\nSee [master branch](https://github.com/karenpayneoregon/ClassValidationVisualBasic)\n\nMicrosoft TechNet [.NET: Defensive data programming (Part 4) Data Annotation](https://social.technet.microsoft.com/wiki/contents/articles/53055.net-defensive-data-programming-part-4-data-annotation.aspx)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fclassvalidationvisualbasic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarenpayneoregon%2Fclassvalidationvisualbasic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fclassvalidationvisualbasic/lists"}