From 8c54f47f887c324a14165fd3b4dddffeecd2c116 Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Mon, 24 Aug 2026 14:06:24 +0200 Subject: [PATCH] FluentValidation validators: migrate the sample into CodeMazeGuides on net10.0 Moves the sample for 'FluentValidation Validators in C#: Built-in and Custom' out of the standalone CodeMazeBlog/fluent-validation-aspnetcore repository and into dotnet-client-libraries/FluentValidationValidators, so the per-article CI gate builds and tests it like every other sample. - netcoreapp3.1 -> net10.0 for both projects; FluentValidation 9.0.1 -> 12.1.1. - Startup.cs and the generic host replaced by minimal hosting in Program.cs. - CascadeMode.StopOnFirstFailure -> CascadeMode.Stop. The old member is removed in 12.x, not deprecated, so the original line no longer compiles. - FluentValidation.AspNetCore dropped: it stops at 11.3.1 and cannot pair with core 12.1.1, and its automatic-validation pipeline is the approach the maintainers no longer recommend for new projects. Validators are registered with AddValidatorsFromAssemblyContaining() and invoked explicitly in OrdersController. - New Tests project (MSTest) covering the custom FullName() message, cascade behaviour, IsInEnum(), RuleForEach() indexing, and the exact strings EmailAddress() accepts and rejects. --- .../ClassLibrary1/ClassLibrary1.csproj | 12 +++ .../FluentValidationExtensions.cs | 14 +++ .../ClassLibrary1/Order.cs | 23 +++++ .../ClassLibrary1/OrderValidator.cs | 16 ++++ .../ClassLibrary1/ProductValidator.cs | 12 +++ .../FluentValidationValidators.sln | 62 +++++++++++++ .../FluentValidationValidators/README.md | 31 +++++++ .../Tests/BuiltInValidatorBehaviourTests.cs | 50 +++++++++++ .../Tests/OrderValidatorTests.cs | 87 +++++++++++++++++++ .../Tests/Tests.csproj | 26 ++++++ .../Controllers/OrdersController.cs | 36 ++++++++ .../Controllers/WeatherForecastController.cs | 26 ++++++ .../WebApplication1/Program.cs | 20 +++++ .../Properties/launchSettings.json | 30 +++++++ .../WebApplication1/WeatherForecast.cs | 13 +++ .../WebApplication1/WebApplication1.csproj | 23 +++++ .../appsettings.Development.json | 9 ++ .../WebApplication1/appsettings.json | 10 +++ dotnet-client-libraries/README.md | 1 + 19 files changed, 501 insertions(+) create mode 100644 dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ClassLibrary1.csproj create mode 100644 dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/FluentValidationExtensions.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/Order.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/OrderValidator.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ProductValidator.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/FluentValidationValidators.sln create mode 100644 dotnet-client-libraries/FluentValidationValidators/README.md create mode 100644 dotnet-client-libraries/FluentValidationValidators/Tests/BuiltInValidatorBehaviourTests.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/Tests/OrderValidatorTests.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/Tests/Tests.csproj create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/OrdersController.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/WeatherForecastController.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/Program.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/Properties/launchSettings.json create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/WeatherForecast.cs create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/WebApplication1.csproj create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.Development.json create mode 100644 dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.json diff --git a/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ClassLibrary1.csproj b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ClassLibrary1.csproj new file mode 100644 index 0000000000..b16aab1dd3 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ClassLibrary1.csproj @@ -0,0 +1,12 @@ + + + + net10.0 + enable + + + + + + + diff --git a/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/FluentValidationExtensions.cs b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/FluentValidationExtensions.cs new file mode 100644 index 0000000000..c828e9d439 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/FluentValidationExtensions.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace ClassLibrary1 +{ + public static class FluentValidationExtensions + { + public static IRuleBuilderOptions FullName(this IRuleBuilder ruleBuilder) + { + return ruleBuilder.MinimumLength(10) + .Must(val => val.Split(" ").Length == 2) + .WithMessage("Name must contain a single space and be at least 10 characters long"); + } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/Order.cs b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/Order.cs new file mode 100644 index 0000000000..86b3a045bf --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/Order.cs @@ -0,0 +1,23 @@ +namespace ClassLibrary1 +{ + public class Order + { + public string CustomerName { get; set; } + public int Price { get; set; } + public string CustomerEmail { get; set; } + public OrderStatus OrderStatus { get; set; } + public Product[] Products { get; set; } + } + + public class Product + { + public string Name { get; set; } + } + + public enum OrderStatus + { + Accepted, + Processing, + Complete + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/OrderValidator.cs b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/OrderValidator.cs new file mode 100644 index 0000000000..827498464a --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/OrderValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace ClassLibrary1 +{ + public class OrderValidator : AbstractValidator + { + public OrderValidator() + { + RuleFor(model => model.CustomerName).FullName(); + RuleFor(model => model.CustomerEmail).Cascade(CascadeMode.Stop).EmailAddress().MinimumLength(20); + RuleFor(model => model.Price).InclusiveBetween(1, 1000); + RuleFor(model => model.OrderStatus).IsInEnum(); + RuleForEach(model => model.Products).SetValidator(new ProductValidator()); + } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ProductValidator.cs b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ProductValidator.cs new file mode 100644 index 0000000000..60ae28e0ba --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/ClassLibrary1/ProductValidator.cs @@ -0,0 +1,12 @@ +using FluentValidation; + +namespace ClassLibrary1 +{ + public class ProductValidator : AbstractValidator + { + public ProductValidator() + { + RuleFor(model => model.Name).NotEmpty(); + } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/FluentValidationValidators.sln b/dotnet-client-libraries/FluentValidationValidators/FluentValidationValidators.sln new file mode 100644 index 0000000000..ea22e4e28e --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/FluentValidationValidators.sln @@ -0,0 +1,62 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1\ClassLibrary1.csproj", "{D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApplication1", "WebApplication1\WebApplication1.csproj", "{209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{A5C5A8F0-33DE-422D-B41D-811514D33353}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Debug|x64.ActiveCfg = Debug|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Debug|x64.Build.0 = Debug|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Debug|x86.ActiveCfg = Debug|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Debug|x86.Build.0 = Debug|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Release|Any CPU.Build.0 = Release|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Release|x64.ActiveCfg = Release|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Release|x64.Build.0 = Release|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Release|x86.ActiveCfg = Release|Any CPU + {D53ED5E5-4F4D-4B88-8D63-B1A11CC83475}.Release|x86.Build.0 = Release|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Debug|x64.ActiveCfg = Debug|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Debug|x64.Build.0 = Debug|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Debug|x86.ActiveCfg = Debug|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Debug|x86.Build.0 = Debug|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Release|Any CPU.Build.0 = Release|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Release|x64.ActiveCfg = Release|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Release|x64.Build.0 = Release|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Release|x86.ActiveCfg = Release|Any CPU + {209EDB45-BFE8-4F1B-A0B9-9EF30A3D4D6D}.Release|x86.Build.0 = Release|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Debug|x64.Build.0 = Debug|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Debug|x86.Build.0 = Debug|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Release|Any CPU.Build.0 = Release|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Release|x64.ActiveCfg = Release|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Release|x64.Build.0 = Release|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Release|x86.ActiveCfg = Release|Any CPU + {A5C5A8F0-33DE-422D-B41D-811514D33353}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/dotnet-client-libraries/FluentValidationValidators/README.md b/dotnet-client-libraries/FluentValidationValidators/README.md new file mode 100644 index 0000000000..63cc702e98 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/README.md @@ -0,0 +1,31 @@ +## FluentValidation Validators in C#: Built-in and Custom + +Source code for [FluentValidation Validators in C#: Built-in and Custom](https://code-maze.com/deep-dive-validators-fluentvalidation/). + +| Folder | What it is | +| - | - | +| `ClassLibrary1` | The validators themselves — `Order`, `Product`, `OrderValidator`, `ProductValidator`, and the `FullName()` custom validator written as an extension method on `IRuleBuilder`. | +| `WebApplication1` | An ASP.NET Core API that references the class library, registers every validator with `AddValidatorsFromAssemblyContaining()`, and invokes `IValidator` explicitly in `OrdersController`. | +| `Tests` | Tests over the validators: the custom `FullName()` message, `CascadeMode.Stop` reporting one failure instead of two, `IsInEnum()` rejecting an undeclared member, `RuleForEach()` reporting the item index, and the exact strings `EmailAddress()` does and does not accept. | + +Everything targets .NET 10 and FluentValidation 12. + +``` +dotnet build FluentValidationValidators.sln +dotnet test FluentValidationValidators.sln +``` + +### A note on `FluentValidation.AspNetCore` + +The API project does **not** reference `FluentValidation.AspNetCore`, and this is +deliberate on two counts. + +The package stops at **11.3.1** — there is no 12.x — so it cannot be paired with +FluentValidation 12.1.1. And the automatic-validation pipeline it provides is the +approach FluentValidation's own ASP.NET Core documentation says it "no longer +recommend[s] ... for new projects", while still supporting it for legacy code. + +The current path is the one this sample uses: register the validators with +`AddValidatorsFromAssemblyContaining()` from +`FluentValidation.DependencyInjectionExtensions`, inject `IValidator`, and call +`ValidateAsync()` where the validation belongs. diff --git a/dotnet-client-libraries/FluentValidationValidators/Tests/BuiltInValidatorBehaviourTests.cs b/dotnet-client-libraries/FluentValidationValidators/Tests/BuiltInValidatorBehaviourTests.cs new file mode 100644 index 0000000000..9ee0fe5b47 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/Tests/BuiltInValidatorBehaviourTests.cs @@ -0,0 +1,50 @@ +using FluentValidation; + +namespace Tests; + +/// +/// These tests pin the two library behaviours the article states as facts, so that a package +/// bump that changes either of them fails CI instead of silently making the article wrong. +/// +[TestClass] +public class BuiltInValidatorBehaviourTests +{ + private class EmailHolder + { + public string Email { get; set; } + } + + private class EmailHolderValidator : AbstractValidator + { + public EmailHolderValidator() => RuleFor(model => model.Email).EmailAddress(); + } + + private static bool IsAcceptedAsEmail(string value) + => new EmailHolderValidator().Validate(new EmailHolder { Email = value }).IsValid; + + [TestMethod] + [DataRow("joebloggs@someemaildomain.com")] + [DataRow("a@b")] + [DataRow("has space@x.com")] + public void EmailAddressValidator_AcceptsAnythingWithASingleAtSign(string value) + { + Assert.IsTrue(IsAcceptedAsEmail(value)); + } + + [TestMethod] + [DataRow("not-an-email")] + [DataRow("plain")] + [DataRow("a@b@c")] + public void EmailAddressValidator_RejectsValuesWithoutExactlyOneAtSign(string value) + { + Assert.IsFalse(IsAcceptedAsEmail(value)); + } + + [TestMethod] + public void CascadeMode_DeclaresOnlyContinueAndStop() + { + CollectionAssert.AreEquivalent( + new[] { "Continue", "Stop" }, + Enum.GetNames()); + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/Tests/OrderValidatorTests.cs b/dotnet-client-libraries/FluentValidationValidators/Tests/OrderValidatorTests.cs new file mode 100644 index 0000000000..c86869bb3a --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/Tests/OrderValidatorTests.cs @@ -0,0 +1,87 @@ +using ClassLibrary1; +using FluentValidation; + +namespace Tests; + +[TestClass] +public class OrderValidatorTests +{ + private readonly OrderValidator _validator = new(); + + private static Order ValidOrder() => new() + { + CustomerName = "Joe Bloggsworth", + CustomerEmail = "joebloggs@someemaildomain.com", + Price = 100, + OrderStatus = OrderStatus.Accepted, + Products = [new Product { Name = "Keyboard" }] + }; + + [TestMethod] + public void WhenOrderIsValid_ThenValidationSucceeds() + { + var result = _validator.Validate(ValidOrder()); + + Assert.IsTrue(result.IsValid); + Assert.AreEqual(0, result.Errors.Count); + } + + [TestMethod] + public void WhenCustomerNameHasNoSpace_ThenFullNameValidatorReportsItsOwnMessage() + { + var order = ValidOrder(); + order.CustomerName = "JoeBloggsworth"; + + var result = _validator.Validate(order); + + Assert.IsFalse(result.IsValid); + var failure = result.Errors.Single(e => e.PropertyName == nameof(Order.CustomerName)); + Assert.AreEqual("Name must contain a single space and be at least 10 characters long", failure.ErrorMessage); + } + + [TestMethod] + public void WhenCascadeModeIsStop_ThenOnlyTheFirstEmailFailureIsReported() + { + var order = ValidOrder(); + order.CustomerEmail = "AAAAA"; + + var result = _validator.Validate(order); + + var emailFailures = result.Errors.Where(e => e.PropertyName == nameof(Order.CustomerEmail)).ToList(); + Assert.AreEqual(1, emailFailures.Count); + StringAssert.Contains(emailFailures[0].ErrorMessage, "valid email address"); + } + + [TestMethod] + public void WhenOrderStatusIsNotADeclaredMember_ThenIsInEnumRejectsIt() + { + var order = ValidOrder(); + order.OrderStatus = (OrderStatus)42; + + var result = _validator.Validate(order); + + Assert.IsFalse(result.IsValid); + Assert.IsTrue(result.Errors.Any(e => e.PropertyName == nameof(Order.OrderStatus))); + } + + [TestMethod] + public void WhenOneProductIsInvalid_ThenRuleForEachReportsTheItemIndex() + { + var order = ValidOrder(); + order.Products = [new Product { Name = "Keyboard" }, new Product { Name = string.Empty }]; + + var result = _validator.Validate(order); + + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Products[1].Name", result.Errors.Single().PropertyName); + } + + [TestMethod] + public void WhenValidationFails_ThenValidateAndThrowThrowsValidationException() + { + var order = ValidOrder(); + order.Price = 5000; + + Assert.ThrowsExactly(() => _validator.ValidateAndThrow(order)); + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/Tests/Tests.csproj b/dotnet-client-libraries/FluentValidationValidators/Tests/Tests.csproj new file mode 100644 index 0000000000..2e50ee6e59 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/Tests/Tests.csproj @@ -0,0 +1,26 @@ + + + + net10.0 + enable + + false + true + + + + + + + + + + + + + + + + + + diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/OrdersController.cs b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/OrdersController.cs new file mode 100644 index 0000000000..a7033d7dc0 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/OrdersController.cs @@ -0,0 +1,36 @@ +using ClassLibrary1; +using FluentValidation; +using Microsoft.AspNetCore.Mvc; + +namespace WebApplication1.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class OrdersController : ControllerBase + { + private readonly IValidator _validator; + + public OrdersController(IValidator validator) + { + _validator = validator; + } + + [HttpPost] + public async Task Post([FromBody] Order order) + { + var validationResult = await _validator.ValidateAsync(order); + + if (!validationResult.IsValid) + { + foreach (var error in validationResult.Errors) + { + ModelState.AddModelError(error.PropertyName, error.ErrorMessage); + } + + return ValidationProblem(ModelState); + } + + return Ok("Success!"); + } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/WeatherForecastController.cs b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000000..d0c0b03b44 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Controllers/WeatherForecastController.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WebApplication1.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = + [ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + ]; + + [HttpGet] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Program.cs b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Program.cs new file mode 100644 index 0000000000..7672195172 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Program.cs @@ -0,0 +1,20 @@ +using ClassLibrary1; +using FluentValidation; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); +builder.Services.AddValidatorsFromAssemblyContaining(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} + +app.UseHttpsRedirection(); +app.UseAuthorization(); +app.MapControllers(); + +app.Run(); diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Properties/launchSettings.json b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Properties/launchSettings.json new file mode 100644 index 0000000000..58ad4e054c --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53840", + "sslPort": 44314 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApplication1": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/WeatherForecast.cs b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/WeatherForecast.cs new file mode 100644 index 0000000000..41c1e7e907 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace WebApplication1 +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/WebApplication1.csproj b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/WebApplication1.csproj new file mode 100644 index 0000000000..ce98df8569 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/WebApplication1.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + enable + + + + + + + + + + + + diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.Development.json b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.Development.json new file mode 100644 index 0000000000..8983e0fc1c --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.json b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.json new file mode 100644 index 0000000000..d9d9a9bff6 --- /dev/null +++ b/dotnet-client-libraries/FluentValidationValidators/WebApplication1/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/dotnet-client-libraries/README.md b/dotnet-client-libraries/README.md index 1f5b8dfac6..d232baf3a6 100644 --- a/dotnet-client-libraries/README.md +++ b/dotnet-client-libraries/README.md @@ -59,3 +59,4 @@ This section contains the topics about the client libraries in .NET. - [Introduction to the Wolverine Library in .NET](https://code-maze.com/dotnet-wolverine-library/) - [Comparison of Rebus, NServiceBus, and MassTransit in .NET](https://code-maze.com/aspnetcore-comparison-of-rebus-nservicebus-and-masstransit/) - [Polly in .NET: Retry, Circuit Breaker, and Fallback](https://code-maze.com/creating-resilient-microservices-in-net-with-polly/) +- [FluentValidation Validators in C#: Built-in and Custom](https://code-maze.com/deep-dive-validators-fluentvalidation/)