### Complete Example with [Validate] and Custom Logic Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validate-attribute.md This example demonstrates a complete C# record `CreateUserRequest` using the [Validate] attribute, including various built-in validators and a custom `AdditionalValidations` method for business logic. It also shows how to use the generated `Validate` method and handle the `ValidationResult`. ```csharp using Immediate.Validations.Shared; [Validate] public partial record CreateUserRequest : IValidationTarget { [NotNull] [MinLength(1)] public required string FirstName { get; init; } [NotNull] [MinLength(1)] public required string LastName { get; init; } [Match(expr: @"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")] public required string Email { get; init; } [GreaterThan(0)] [LessThan(150)] public required int Age { get; init; } private static void AdditionalValidations( ValidationResult errors, CreateUserRequest target) { // Custom business logic validation if (target.Email.EndsWith("@internal.com") && !target.FirstName.StartsWith("Admin")) { errors.Add("Email", "Internal email requires admin privileges"); } } } // Usage: var request = new CreateUserRequest { FirstName = "John", LastName = "Doe", Email = "john@example.com", Age = 25 }; var result = CreateUserRequest.Validate(request); if (!result.IsValid) { foreach (var error in result.Errors) { Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}"); } } ``` -------------------------------- ### Example ValidationException Message Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-exception.md An example illustrating the structure of a ValidationException message. ```plaintext Validation failed: -- Email: Email must not be null. -- Age: Age must be greater than 18 ``` -------------------------------- ### Examples of Valid Validation Expressions Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Demonstrates correct usage of `ValidationResult.Add()` with lambda expressions that adhere to the required pattern. These examples show how to correctly invoke static `ValidateProperty` methods from various validator attributes. ```csharp // ✓ Correct result.Add(() => NotNullAttribute.ValidateProperty(user.Email)); result.Add(() => MinLengthAttribute.ValidateProperty(user.Name, 3)); result.Add(() => GreaterThanAttribute.ValidateProperty(user.Age, 18)); ``` -------------------------------- ### Validate() Method Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md Demonstrates how to use the Validate() method on a class implementing IValidationTarget to check its own validity. Requires the class to implement IValidationTarget. ```csharp public partial record User : IValidationTarget { public required string Email { get; init; } // Validation members generated by source generator } var user = new User { Email = "" }; var result = user.Validate(); if (!result.IsValid) { // Handle errors } ``` -------------------------------- ### Thread-Safe Validation Configuration Initialization Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/configuration.md Configure validation using a thread-safe ValidationConfiguration.Localizer. This setup should occur during application startup. ```csharp // ValidationConfiguration is thread-safe for getting the Localizer // but setting should happen during application startup var initializer = new ValidationInitializer(config); initializer.ConfigureValidation(); public class ValidationInitializer { private readonly IConfiguration _config; public ValidationInitializer(IConfiguration config) => _config = config; public void ConfigureValidation() { var factory = _config.GetSection("Validation") .Get(); ValidationConfiguration.Localizer = factory.CreateLocalizer(); } } ``` -------------------------------- ### Configuration File-Based Validation Setup Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/configuration.md Set up the validation localizer by reading the 'Validation:LocalizerType' from configuration. Supports 'Custom', 'Verbose', or a default ValidatorLocalizer. ```csharp var localizerType = builder.Configuration["Validation:LocalizerType"]; var localizer = localizerType switch { "Custom" => new CustomLocalizer(), "Verbose" => new VerboseLocalizer(), _ => new ValidatorLocalizer(), // Default }; ValidationConfiguration.Localizer = localizer; ``` -------------------------------- ### Implement a Multi-Language Localizer Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/configuration.md Create a localizer that supports multiple languages. This example defines messages for different cultures and selects the appropriate language based on the current UI culture. ```csharp public class MultiLanguageLocalizer : IStringLocalizer { private readonly Dictionary> _locales = new() { ["en"] = new() { ["NotNullAttribute"] = new("Field is required", false), ["MinLengthAttribute"] = new("Minimum {MinLengthValue} characters", false), }, ["es"] = new() { ["NotNullAttribute"] = new("Campo requerido", false), ["MinLengthAttribute"] = new("Mínimo {MinLengthValue} caracteres", false), }, ["fr"] = new() { ["NotNullAttribute"] = new("Champ requis", false), ["MinLengthAttribute"] = new("Minimum {MinLengthValue} caractères", false), }, }; public LocalizedString this[string name] { get { var culture = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName; if (!_locales.ContainsKey(culture)) culture = "en"; if (_locales[culture].TryGetValue(name, out var msg)) return msg; return new LocalizedString(name, name, true); } } public LocalizedString this[string name, params object[] arguments] => this[name]; public IEnumerable GetAllStrings(bool includeParentCultures) { var culture = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName; if (!_locales.ContainsKey(culture)) culture = "en"; return _locales[culture].Values; } } ValidationConfiguration.Localizer = new MultiLanguageLocalizer(); ``` -------------------------------- ### Complete Validator Attribute Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validator-attribute.md A comprehensive example showcasing various validator attributes applied to properties of a RegisterRequest record. This includes NotNull, MinLength, MaxLength, Match, GreaterThan, LessThan, and OneOf attributes, along with a custom additional validation method. ```csharp using Immediate.Validations.Shared; [Validate] public partial record RegisterRequest : IValidationTarget { [NotNull] [MinLength(2)] [MaxLength(50)] public required string FirstName { get; init; } [NotNull] [MinLength(2)] [MaxLength(50)] public required string LastName { get; init; } [Match( expr: @"^[^\s@]+@[^\s@]+\\.[^\s@]+$", Message = "'{PropertyName}' must be a valid email address" )] public required string Email { get; init; } [GreaterThan(17)] [LessThan(120)] public required int Age { get; init; } [OneOf("US", "CA", "MX")] public required string Country { get; init; } private static void AdditionalValidations( ValidationResult errors, RegisterRequest request) { if (string.Equals(request.FirstName, request.LastName)) { errors.Add("LastName", "Last name must be different from first name"); } } } ``` -------------------------------- ### Simple Custom Validator Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validator-attribute.md Demonstrates creating a simple custom validator 'IsEvenAttribute' that checks if an integer is even. Includes usage with the [Validate] attribute. ```csharp using Immediate.Validations.Shared; public sealed class IsEvenAttribute : ValidatorAttribute { public static bool ValidateProperty(int value) => value % 2 == 0; public static string DefaultMessage => ValidationConfiguration.Localizer[nameof(IsEvenAttribute)].Value; } // Usage: [Validate] public partial record EvenNumberRequest : IValidationTarget { [IsEven(Message = "The number must be even")] public required int Number { get; init; } } ``` -------------------------------- ### Custom Localizer Implementation Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-configuration.md Provides an example of a custom IStringLocalizer implementation to define specific error messages for validation attributes. ```csharp using Microsoft.Extensions.Localization; using Immediate.Validations.Shared; public class CustomLocalizer : IStringLocalizer { private readonly Dictionary _messages = new() { ["GreaterThanAttribute"] = new("Age must be older than {ComparisonValue}", false), ["NotNullAttribute"] = new("This field is required", false), ["MinLengthAttribute"] = new("Minimum length is {MinLengthValue}", false), // ... other messages }; public LocalizedString this[string name] => _messages.TryGetValue(name, out var value) ? value : new LocalizedString(name, name, true); public LocalizedString this[string name, params object[] arguments] => this[name]; public IEnumerable GetAllStrings(bool includeParentCultures) => _messages.Values; } // Set the custom localizer ValidationConfiguration.Localizer = new CustomLocalizer(); ``` -------------------------------- ### Examples of Invalid Validation Expressions Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Provides examples of lambda expressions that will cause a `NotSupportedException` when used with `ValidationResult.Add()`. These include expressions that do not return a boolean, do not call a static method, or do not follow the `ValidatorAttribute.ValidateProperty` pattern. ```csharp // ✗ Wrong: method doesn't return bool result.Add(() => CustomAttribute.ValidateProperty(value)); // ✗ Wrong: not a ValidatorAttribute result.Add(() => SomeMethod(value)); // ✗ Wrong: not a method call expression result.Add(() => true); ``` -------------------------------- ### Custom Validator with Parameters Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validator-attribute.md Shows how to create a custom validator 'DivisibleByAttribute' that accepts parameters, like a divisor. Includes usage with the [Validate] attribute. ```csharp public sealed class DivisibleByAttribute( [TargetType] object divisor) : ValidatorAttribute { public static bool ValidateProperty(int value, int divisor) => value % divisor == 0; public static string DefaultMessage => "'{PropertyName}' must be divisible by {DivisorValue}."; } // Usage: [Validate] public partial record Request : IValidationTarget { [DivisibleBy(3, Message = "' {PropertyName}' must be divisible by 3")] public required int Number { get; init; } } ``` -------------------------------- ### NotNullAttribute Failure Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Demonstrates the failure case for NotNullAttribute when a required property is null. The default error message is '`{PropertyName}` must not be null.' ```csharp [Validate] public partial record User : IValidationTarget { [NotNull] public required string Email { get; init; } } // Failure case: var user = new User { Email = null }; var result = User.Validate(user); // Error: "Email must not be null." ``` -------------------------------- ### MatchAttribute Failure Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Demonstrates the failure case for MatchAttribute when a string does not conform to the specified regular expression pattern. The default error message is '`{PropertyName}` is not in the correct format.' ```csharp [Match(expr: @"^\d{3}-\d{4}$")] public required string Phone { get; init; } // Failure: Phone = "1234567" (doesn't match pattern) // Error: "Phone is not in the correct format." ``` -------------------------------- ### Implement Custom Business Logic Validation Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Shows how to implement custom validation logic that goes beyond standard attributes. This example adds a validation rule to ensure the first and last names are different, using the AdditionalValidations method. ```csharp [Validate] public partial record Request : IValidationTarget { public required string FirstName { get; init; } public required string LastName { get; init; } private static void AdditionalValidations( ValidationResult errors, Request target) { if (target.FirstName == target.LastName) { errors.Add( "LastName", "Last name must be different from first name" ); } } } var request = new Request { FirstName = "John", LastName = "John" }; var result = Request.Validate(request); // result.Errors: // PropertyName: "LastName", Message: "Last name must be different from first name" ``` -------------------------------- ### Conceptual Generated Code for [Validate] Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validate-attribute.md This conceptual example illustrates the C# code that the source generator produces for a class decorated with the [Validate] attribute. It shows the generated static `Validate` methods that incorporate property-level validations and a placeholder for additional custom logic. ```csharp [Validate] public partial record User : IValidationTarget { [NotNull] [MinLength(1)] public required string Email { get; init; } [GreaterThan(0)] public required int Age { get; init; } // Source generator produces: public static ValidationResult Validate(User? target) => Validate(target, new ValidationResult()); public static ValidationResult Validate(User? target, ValidationResult errors) { if (target is null) return errors; errors.Add(() => NotNullAttribute.ValidateProperty(target.Email)); errors.Add(() => MinLengthAttribute.ValidateProperty(target.Email, 1)); errors.Add(() => GreaterThanAttribute.ValidateProperty(target.Age, 0)); AdditionalValidations(errors, target); return errors; } partial void AdditionalValidations(ValidationResult errors, User target); } ``` -------------------------------- ### ThrowIfInvalid(T obj, string message) Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-exception.md Use this method to validate an object and provide a custom message for the ValidationException if validation fails. ```csharp var user = new User { Email = null }; ValidationException.ThrowIfInvalid(user, "User validation failed"); // Throws with custom title in the exception ``` -------------------------------- ### OneOfAttribute Failure Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Illustrates a failure scenario for the OneOfAttribute when the provided string value is not present in the list of allowed values. This ensures that string properties adhere to a predefined set of options. ```csharp [OneOf("Red", "Green", "Blue")] public required string Color { get; init; } // Failure: Color = "Yellow" // Error: "Color was not one of the specified values: ..." ``` -------------------------------- ### MinLengthAttribute Failure Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Shows the failure case for MinLengthAttribute when a string's length is less than the minimum required. The default error message is '`{PropertyName}` must be more than {MinLengthValue} characters.' ```csharp [MinLength(5)] public required string Name { get; init; } // Failure: Name = "Bob" (length 3) // Error: "Name must be more than 5 characters." ``` -------------------------------- ### Validator Instantiation from Naming Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validator-attribute.md Demonstrates how the source generator locates property validators by using nameof() references to static members. This example shows a property with a Match attribute that references a static Regex method. ```csharp [Validate] public partial record Query : IValidationTarget { [GeneratedRegex(@"^\\d+$")] private static partial Regex DigitsOnlyRegex(); [Match(regex: nameof(DigitsOnlyRegex))] public required string ProductId { get; init; } } ``` -------------------------------- ### Generic Custom Validator Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validator-attribute.md Illustrates creating a generic custom validator 'InRangeAttribute' for type-safe range checking. Includes usage with the [Validate] attribute. ```csharp public sealed class InRangeAttribute( [TargetType] object min, [TargetType] object max) : ValidatorAttribute where T : IComparable { public static bool ValidateProperty(T? value, T min, T max) => value is not null && value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0; public static string DefaultMessage => "'{PropertyName}' must be between {MinValue} and {MaxValue}."; } // Usage: [Validate] public partial record PriceRequest : IValidationTarget { [InRange(0, 9999.99)] public required decimal Price { get; init; } } ``` -------------------------------- ### ThrowIfInvalid(T obj) Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-exception.md Use this method to validate an object that implements IValidationTarget. It throws a ValidationException if the object's validation fails, using a default title. ```csharp var user = new User { Email = null }; ValidationException.ThrowIfInvalid(user); // Throws: ValidationException with default "Validation failed" title ``` -------------------------------- ### ThrowIfInvalid(T obj, Func messageFunc) Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-exception.md Use this method to validate an object and dynamically generate the exception message using a provided function based on the object's state. ```csharp var user = new User { Id = 123, Email = null }; ValidationException.ThrowIfInvalid(user, u => $"User {u.Id} has invalid data"); // Throws with: "User 123 has invalid data" ``` -------------------------------- ### Add Property-Specific Validations Source: https://github.com/immediateplatform/immediate.validations/blob/main/readme.md Use attributes like GreaterThan to enforce specific validation rules on properties. This example ensures the 'Id' property is greater than 0. ```csharp [Validate] public partial record Query : IValidationTarget { [GreaterThan(0)] public required int Id { get; init; } } ``` -------------------------------- ### Validate(ValidationResult errors) Method Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md Shows how to use the Validate(ValidationResult errors) method to validate an instance and append any found errors to an existing ValidationResult object. This is useful for accumulating errors from multiple validation targets. ```csharp var user = new User { Email = "" }; var companyErrors = company.Validate(); var userErrors = user.Validate(companyErrors); if (!userErrors.IsValid) { foreach (var error in userErrors.Errors) { Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}"); } } ``` -------------------------------- ### GreaterThanAttribute Failure Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Illustrates the failure case for GreaterThanAttribute when the value is not greater than the specified comparison value. The default error message is '`{PropertyName}` must be greater than `'{ComparisonValue}'`.' ```csharp [GreaterThan(0)] public required int Count { get; init; } // Failure: Count = 0 // Error: "Count must be greater than '0'." ``` -------------------------------- ### EnumValueAttribute Failure Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Demonstrates a failure case for the EnumValueAttribute where the provided enum value is not defined within the enum's range. This typically occurs when an unexpected integer is assigned to an enum property. ```csharp public enum Status { Active = 1, Inactive = 2 } [Validate] public partial record Request : IValidationTarget { [EnumValue] public required Status Current { get; init; } } // Failure: Current = (Status)99 // Error: "Current has a range of values which does not include '99'." ``` -------------------------------- ### Reference Other Properties in Validations Source: https://github.com/immediateplatform/immediate.validations/blob/main/readme.md Use nameof() to reference static and instance properties, fields, or methods within validation attributes when direct referencing is not possible. This example uses a generated regex for validation. ```csharp [Validate] public partial record Query : IValidationTarget { [GeneratedRegex(@"^\\d+$")] private static partial Regex AllDigitsRegex(); [Match(regex: nameof(AllDigitsRegex))] public required string Id { get; init; } } ``` -------------------------------- ### Import Immediate.Validations Shared Namespace Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/README.md Include this using statement to access the core types of the Immediate.Validations library. ```csharp using Immediate.Validations.Shared; ``` -------------------------------- ### Create ValidationError Instances Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-error.md Demonstrates how to instantiate and initialize ValidationError objects with specific property names and error messages. ```csharp var error = new ValidationError { PropertyName = "Email", ErrorMessage = "Email address is required" }; var error2 = new ValidationError { PropertyName = "User.Profile.Age", ErrorMessage = "'Age' must be greater than 18" }; ``` -------------------------------- ### ThrowIfInvalid(ValidationResult errors, string message) Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-exception.md Use this method to throw a ValidationException with a custom title when a ValidationResult object contains errors. ```csharp var result = MyClass.Validate(instance); ValidationException.ThrowIfInvalid(result, "Request validation failed"); ``` -------------------------------- ### API Reference Overview Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/INDEX.txt This section outlines the structure and content of the API reference documentation, indicating which interfaces, attributes, and core classes are fully documented and the depth of coverage provided for each. ```APIDOC ## API Reference Structure This documentation provides detailed API references for the Immediate.Validations library, organized into several key areas: ### API Reference Files: - `validation-result.md`: Details the `ValidationResult` class for accumulating errors. - `validation-error.md`: Describes the `ValidationError` record for single failures. - `validation-exception.md`: Explains the `ValidationException` type. - `ivalidation-target.md`: Covers `IValidationTarget` interfaces (instance-based and static-based). - `validate-attribute.md`: Documents the `[Validate]` attribute for code generation. - `validator-attribute.md`: Details the `ValidatorAttribute` base class and built-in validators. - `target-type-attribute.md`: Explains the `[TargetType]` attribute. - `validation-behavior.md`: Covers `ValidationBehavior` for Immediate.Handlers. - `validation-configuration.md`: Details `ValidationConfiguration` for localization. ### Documented Interfaces: - `IValidationTarget` (instance-based) - `IValidationTarget` (static-based) ### Documented Attributes: - `[Validate]`, `[NotNull]`, `[Empty]`, `[NotEmpty]`, `[GreaterThan]`, `[GreaterThanOrEqual]`, `[LessThan]`, `[LessThanOrEqual]`, `[Equal]`, `[NotEqual]`, `[MinLength]`, `[MaxLength]`, `[Length]`, `[Match]`, `[EnumValue]`, `[OneOf]`, `[TargetType]` ### Documented Core Classes: - `ValidationResult` - `ValidationError` - `ValidationException` - `ValidationBehavior` - `ValidationConfiguration` ### Documentation Content Depth: Each API reference file includes full type definitions, property/method signatures, parameter tables, return types, exception details, usage examples, and source file references. All public APIs are documented with full signatures, parameter tables, return types, exception conditions, code examples, source file paths, and related types. ``` -------------------------------- ### Integration with ASP.NET Core Localization Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-configuration.md Shows how to integrate a custom localizer with ASP.NET Core's dependency injection and localization services for use with ValidationConfiguration. ```csharp using Microsoft.Extensions.Localization; using Immediate.Validations.Shared; // In Program.cs var builder = WebApplication.CreateBuilder(args); // Configure localization builder.Services.AddLocalization(); builder.Services.Configure(options => { var supportedCultures = new[] { "en", "es", "fr" }; options.SetDefaultCulture("en"); options.AddSupportedCultures(supportedCultures); options.AddSupportedUICultures(supportedCultures); }); // Custom localizer that works with DI public class DependencyInjectionLocalizer : IStringLocalizer { private readonly IStringLocalizer _baseLocalizer; public DependencyInjectionLocalizer(IStringLocalizerFactory factory) { _baseLocalizer = factory.Create("Validators", "MyApp"); } public LocalizedString this[string name] => _baseLocalizer[name]; public LocalizedString this[string name, params object[] arguments] => _baseLocalizer[name, arguments]; public IEnumerable GetAllStrings(bool includeParentCultures) => _baseLocalizer.GetAllStrings(includeParentCultures); } // Set it as the validator localizer var app = builder.Build(); var localizerFactory = app.Services.GetRequiredService(); ValidationConfiguration.Localizer = new DependencyInjectionLocalizer(localizerFactory); ``` -------------------------------- ### Register Custom Localizer in Startup Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-configuration.md Shows how to register a custom `SpanishValidatorLocalizer` within the application's startup configuration. This ensures that all validators will use the Spanish messages by default. ```csharp public class Startup { public void ConfigureServices(IServiceCollection services) { // ... other configurations // Set Spanish localizer for validators ValidationConfiguration.Localizer = new SpanishValidatorLocalizer(); } } ``` -------------------------------- ### Import for Immediate.Handlers Integration Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/README.md This import is necessary when integrating Immediate.Validations with Immediate.Handlers, enabling the use of behaviors for automatic request validation. ```csharp using Immediate.Handlers.Shared; using Immediate.Validations.Shared; [assembly: Behaviors(typeof(ValidationBehavior<,>))] ``` -------------------------------- ### ThrowIfInvalid(ValidationResult errors) Example Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-exception.md Use this method to throw a ValidationException if a given ValidationResult object indicates that validation has failed (i.e., IsValid is false). ```csharp var result = MyClass.Validate(instance); ValidationException.ThrowIfInvalid(result); // Throws only if result.IsValid is false ``` -------------------------------- ### Creating a Validatable Class with Attributes Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md Define a class implementing IValidationTarget and use attributes like [NotNull], [MinLength], [Match], and [GreaterThan] to specify validation rules. The source generator will automatically create validation logic based on these attributes. ```csharp using Immediate.Validations.Shared; [Validate] public partial record CreateUserRequest : IValidationTarget { [NotNull] [MinLength(1)] public required string FirstName { get; init; } [NotNull] [MinLength(1)] public required string LastName { get; init; } [Match(expr: "^[^"]+@[^"]+$")] public required string Email { get; init; } [GreaterThan(0)] public required int Age { get; init; } } ``` -------------------------------- ### Correct and Incorrect Usage of [Validate] Attribute Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validate-attribute.md Demonstrates the correct and incorrect ways to apply the [Validate] attribute to a class. Ensure the class is partial and implements IValidationTarget. ```csharp // ✓ Correct [Validate] public partial record MyRequest : IValidationTarget { // ... } // ✗ Incorrect - not partial [Validate] public record MyRequest : IValidationTarget { // ... } // ✗ Incorrect - missing IValidationTarget [Validate] public partial record MyRequest { // ... } ``` -------------------------------- ### Manual Validation and Exception Handling Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-result.md Shows how to manually create and populate a ValidationResult, add specific or conditional errors, and throw a ValidationException if validation fails. ```csharp var result = new ValidationResult(); // Add specific validation result.Add("Email", "Email is required"); // Conditional validation if (user.Type == "Premium") { result.Add("PaymentMethod", "Payment method is required for premium accounts"); } if (!result.IsValid) { throw new ValidationException("Validation failed", result.Errors); } ``` -------------------------------- ### Define Default Message for Custom Validator Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Example of how to define the required static string DefaultMessage property for a custom validator. This ensures the validator can provide a default error message. ```csharp public static string DefaultMessage => "Error message"; ``` -------------------------------- ### Creating Validation Errors Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-error.md Demonstrates how to instantiate and populate a ValidationError object. ```APIDOC ## Creating Validation Errors ```csharp var error = new ValidationError { PropertyName = "Email", ErrorMessage = "Email address is required" }; var error2 = new ValidationError { PropertyName = "User.Profile.Age", ErrorMessage = "'Age' must be greater than 18" }; ``` ``` -------------------------------- ### Comparison Validators with Different Types Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/target-type-attribute.md Demonstrates how GreaterThan and LessThan validators work with different numeric and string types by automatically casting the literal values. ```csharp using Immediate.Validations.Shared; [Validate] public partial record NumericConstraints : IValidationTarget { [GreaterThan(0)] // Cast 0 to int public required int IntValue { get; init; } [GreaterThan(0)] // Cast 0 to decimal public required decimal DecimalValue { get; init; } [GreaterThan(0)] // Cast 0 to double public required double DoubleValue { get; init; } [LessThan("Z")] // Cast "Z" to string public required string Letter { get; init; } } ``` -------------------------------- ### Environment-Based Validation Configuration Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/configuration.md Configure the validation localizer based on the application's environment. Use VerboseLocalizer in development and ConciseLocalizer in production. ```csharp if (app.Environment.IsDevelopment()) { // Use verbose messages in development ValidationConfiguration.Localizer = new VerboseLocalizer(); } else { // Use concise messages in production ValidationConfiguration.Localizer = new ConciseLocalizer(); } ``` -------------------------------- ### Instance-Based Validation with IValidationTarget Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md Implement the IValidationTarget interface directly on a record or class to enable instance-based validation. This allows calling a Validate method directly on an object instance, which can include custom validation logic. ```csharp public partial record User : IValidationTarget { [NotNull] public required string Email { get; init; } private void AdditionalValidations(ValidationResult errors) { // Custom validation logic } } var user = new User { Email = "test@example.com" }; var result = user.Validate(); ``` -------------------------------- ### Dynamic Validation Message Generation Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/configuration.md Create a dynamic localizer that can generate validation messages on the fly, potentially loading them from external sources like a database or cache. ```csharp public class DynamicLocalizer : IStringLocalizer { private readonly ILogger _logger; public LocalizedString this[string name] { get { _logger.LogInformation("Localizing message: {MessageKey}", name); // Could load from database, cache, etc. return new ValidatorLocalizer()[name]; } } public LocalizedString this[string name, params object[] arguments] => this[name]; public IEnumerable GetAllStrings(bool includeParentCultures) => new ValidatorLocalizer().GetAllStrings(includeParentCultures); } ``` -------------------------------- ### Implement Custom Spanish Validator Localizer Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-configuration.md Demonstrates how to create a custom `IStringLocalizer` implementation for Spanish validation messages. This custom localizer maps validator attribute names to their Spanish translations. ```csharp public class SpanishValidatorLocalizer : IStringLocalizer { private static readonly Dictionary Spanish = new() { [nameof(NotNullAttribute)] = new("'{{PropertyName}}' es requerido.", false), [nameof(GreaterThanAttribute)] = new("'{{PropertyName}}' debe ser mayor que {{ComparisonValue}}.", false), [nameof(MinLengthAttribute)] = new("'{{PropertyName}}' debe tener al menos {{MinLengthValue}} caracteres.", false), [nameof(MaxLengthAttribute)] = new("'{{PropertyName}}' debe tener como máximo {{MaxLengthValue}} caracteres.", false), // ... other translations }; public LocalizedString this[string name] => Spanish.TryGetValue(name, out var message) ? message : new LocalizedString(name, name, true); public LocalizedString this[string name, params object[] arguments] => this[name]; public IEnumerable GetAllStrings(bool includeParentCultures) => Spanish.Values; } ``` -------------------------------- ### ValidateAttribute with Interface Implementation Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validate-attribute.md Illustrates applying ValidateAttribute to both an interface (IValidatable) and its concrete record implementation. This ensures validation rules defined on the interface are enforced. ```csharp [Validate] public partial interface IValidatable : IValidationTarget { [NotNull] string Name { get; } [GreaterThan(0)] int Id { get; } } [Validate] public partial record ConcreteImplementation : IValidatable, IValidationTarget { public required string Name { get; init; } public required int Id { get; init; } } ``` -------------------------------- ### Basic Usage of ValidateAttribute Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validate-attribute.md Demonstrates applying the ValidateAttribute to a partial record that implements IValidationTarget. The source generator will create validation methods for decorated properties like NotNull and GreaterThan. ```csharp using Immediate.Validations.Shared; [Validate] public partial record User : IValidationTarget { [NotNull] public required string Email { get; init; } [GreaterThan(0)] public required int Age { get; init; } } ``` -------------------------------- ### Integrate ValidationBehavior with Immediate.Handlers Source: https://github.com/immediateplatform/immediate.validations/blob/main/readme.md Add Immediate.Validations to the Immediate.Handlers behaviors pipeline by including ValidationBehavior in the assembly's default Behaviors. ```csharp using Immediate.Validations.Shared; [assembly: Behaviors( typename(ValidationBehavior<,>) )] ``` -------------------------------- ### Register Validation Behavior with Immediate.Handlers Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/README.md Register the ValidationBehavior in your assembly using the [assembly: Behaviors] attribute. This enables automatic validation for handlers. ```csharp [assembly: Behaviors(typeof(ValidationBehavior<,>))] namespace MyApplication; ``` -------------------------------- ### Iterate Validation Errors with foreach Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-result.md Demonstrates how to iterate over ValidationResult as an IEnumerable using a foreach loop to access individual errors. ```csharp var result = MyClass.Validate(instance); foreach (var error in result) { Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}"); } ``` -------------------------------- ### Global Exception Handler with ProblemDetails Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-exception.md Configure global exception handling in ASP.NET Core to customize ProblemDetails for ValidationException, mapping errors to a ValidationProblemDetails object. ```csharp builder.Services.AddProblemDetails(options => { options.CustomizeProblemDetails = context => { if (context.Exception is ValidationException ex) { context.ProblemDetails = new ValidationProblemDetails( ex.Errors .GroupBy(e => e.PropertyName, StringComparer.OrdinalIgnoreCase) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray(), StringComparer.OrdinalIgnoreCase ) ) { Status = StatusCodes.Status400BadRequest, }; } }; }); ``` -------------------------------- ### Implement Automatic Request Validation with ValidationBehavior Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/types.md A behavior for Immediate.Handlers that automatically validates incoming requests. Ensures that requests implementing IValidationTarget are validated before processing. ```csharp public sealed class ValidationBehavior : Behavior where TRequest : IValidationTarget { public override async ValueTask HandleAsync( TRequest request, CancellationToken cancellationToken); } ``` -------------------------------- ### Integration with Immediate.Handlers Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md When using Immediate.Handlers, the ValidationBehavior can be configured to automatically apply validation to incoming requests. This eliminates the need for manual validation calls within your handlers. ```csharp [assembly: Behaviors(typeof(ValidationBehavior<,>))] public class CreateUserHandler : Handler { public override async ValueTask HandleAsync( CreateUserRequest request, CancellationToken cancellationToken) { // ValidationBehavior automatically calls CreateUserRequest.Validate(request) // and throws ValidationException if invalid return new CreateUserResponse { /* ... */ }; } } ``` -------------------------------- ### Usage in ASP.NET Core Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-behavior.md Integrate the handler with an ASP.NET Core controller. Implement a try-catch block to handle ValidationException and return a BadRequest response with validation errors. ```csharp [ApiController] [Route("api/[controller]")] public class UsersController : ControllerBase { private readonly IHandler _handler; public UsersController( IHandler handler) { _handler = handler; } [HttpPost] public async Task Create( [FromBody] CreateUserRequest request, CancellationToken cancellationToken) { try { var response = await _handler.HandleAsync(request, cancellationToken); return Ok(response); } catch (ValidationException ex) { var errors = ex.Errors .GroupBy(e => e.PropertyName, StringComparer.OrdinalIgnoreCase) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray(), StringComparer.OrdinalIgnoreCase ); return BadRequest(new { errors }); } } } ``` -------------------------------- ### Handle ValidationException in ASP.NET Core with ProblemDetails Source: https://github.com/immediateplatform/immediate.validations/blob/main/readme.md Configure ASP.NET Core's ProblemDetails to handle ValidationException, mapping validation errors to a 400 Bad Request response. ```csharp builder.Services.AddProblemDetails(ConfigureProblemDetails); public static void ConfigureProblemDetails(ProblemDetailsOptions options) => options.CustomizeProblemDetails = c => { if (c.Exception is null) return; c.ProblemDetails = c.Exception switch { ValidationException ex => new ValidationProblemDetails( ex .Errors .GroupBy(x => x.PropertyName, StringComparer.OrdinalIgnoreCase) .ToDictionary( x => x.Key, x => x.Select(x => x.ErrorMessage).ToArray(), StringComparer.OrdinalIgnoreCase ) ) { Status = StatusCodes.Status400BadRequest, }, // other exception handling as desired var ex => new ProblemDetails { Detail = "An error has occurred.", Status = StatusCodes.Status500InternalServerError, } }; c.HttpContext.Response.StatusCode = c.ProblemDetails.Status ?? StatusCodes.Status500InternalServerError; }; ``` -------------------------------- ### Throw ValidationException for IValidationTarget Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Call this method to automatically throw a `ValidationException` if the provided object fails its `IValidationTarget.Validate()` check. The exception will contain details of all validation failures. ```csharp ValidationException.ThrowIfInvalid(myObject); ``` -------------------------------- ### Inheritance Hierarchy with [Validate] Attribute Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validate-attribute.md Shows how the [Validate] attribute works with inheritance. Derived classes can inherit validations from base classes, and the SkipSelf option can be used to prevent self-validation. ```csharp [Validate] public partial record BaseRequest : IValidationTarget { [NotNull] public required string BaseProperty { get; init; } } [Validate(SkipSelf = true)] public partial record DerivedRequest : BaseRequest, IValidationTarget { // Inherits validation of BaseProperty from BaseRequest // Does not add additional validations to DerivedRequest properties public string DerivedProperty { get; init; } } ``` -------------------------------- ### ValidateAttribute with Struct Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validate-attribute.md Demonstrates using ValidateAttribute on a partial struct that implements IValidationTarget. This enables source-generated validation for struct properties. ```csharp [Validate] public partial struct Point : IValidationTarget { [GreaterThanOrEqual(0)] public required double X { get; init; } [GreaterThanOrEqual(0)] public required double Y { get; init; } } ``` -------------------------------- ### Validate() Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md Validates the current instance and returns a ValidationResult containing any validation errors. ```APIDOC ## Validate() ### Description Validates the current instance. ### Method ```csharp ValidationResult Validate() ``` ### Returns `ValidationResult` - A result containing any validation errors. ### Example ```csharp public partial record User : IValidationTarget { public required string Email { get; init; } // Validation members generated by source generator } var user = new User { Email = "" }; var result = user.Validate(); if (!result.IsValid) { // Handle errors } ``` ``` -------------------------------- ### Serialize ValidationErrors to JSON Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-error.md Shows how to group and serialize ValidationError objects into a dictionary format suitable for JSON responses. ```csharp var result = MyClass.Validate(instance); // Convert to dictionary format for JSON response var errorDict = result.Errors .GroupBy(e => e.PropertyName, StringComparer.OrdinalIgnoreCase) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray(), StringComparer.OrdinalIgnoreCase ); var json = JsonSerializer.Serialize(errorDict); // Output: {"Email":["Email is required"],"Age":["Age must be at least 18"]} ``` -------------------------------- ### Custom Message with Template Variables Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validator-attribute.md Use template variables like {PropertyName}, {PropertyValue}, and {ComparisonValue} to create dynamic and informative validation error messages. ```csharp [GreaterThan(18, Message = "' {PropertyName} ' (value: {PropertyValue}) must be greater than {ComparisonValue}")] public required int Age { get; init; } ``` -------------------------------- ### Validate Object with Multiple Errors Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/errors.md Demonstrates validating a User object that has multiple properties failing validation. The code shows how to collect and iterate through all validation errors associated with the object. ```csharp var user = new User { Email = null, Age = 15, Name = "" }; var result = User.Validate(user); // result.Errors contains: // 1. PropertyName: "Email", Message: "Email must not be null." // 2. PropertyName: "Age", Message: "Age must be greater than 18." // 3. PropertyName: "Name", Message: "Name must not be empty." ``` ```csharp if (!result.IsValid) { foreach (var error in result.Errors) { Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}"); } } ``` -------------------------------- ### Define Validatable Request Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-behavior.md Define a request object that implements IValidationTarget and is decorated with the [Validate] attribute. Use validation attributes like [NotNull], [MinLength], [Match], and [GreaterThan] to specify validation rules. ```csharp using Immediate.Validations.Shared; [Validate] public partial record CreateUserRequest : IValidationTarget { [NotNull] [MinLength(1)] public required string FirstName { get; init; } [NotNull] [MinLength(1)] public required string LastName { get; init; } [Match(expr: @"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")] public required string Email { get; init; } [GreaterThan(17)] public required int Age { get; init; } } ``` -------------------------------- ### Define a Validatable Class Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/README.md Define a class that can be validated by decorating it with the [Validate] attribute and implementing IValidationTarget. Properties are decorated with specific validator attributes. ```csharp using Immediate.Validations.Shared; [Validate] public partial record CreateUserRequest : IValidationTarget { [NotNull] [MinLength(1)] public required string FirstName { get; init; } [Match(expr: @"^[^\s@]+@[^\s@]+\.[^\s@]+$")] public required string Email { get; init; } [GreaterThan(17)] public required int Age { get; init; } } ``` -------------------------------- ### IValidationTarget Interface Definition Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md Defines the contract for non-generic validation targets, providing methods to validate an instance and accumulate errors. ```csharp public interface IValidationTarget { ValidationResult Validate(); ValidationResult Validate(ValidationResult errors); } ``` -------------------------------- ### Handle Validation Failures with ValidationException Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/types.md An exception thrown when validation fails. Provides static methods to check and throw exceptions based on ValidationResult or IValidationTarget objects. ```csharp public sealed class ValidationException : Exception { public string Title { get; } public IReadOnlyList Errors { get; } public static void ThrowIfInvalid(T obj) where T : IValidationTarget; public static void ThrowIfInvalid(T obj, string message) where T : IValidationTarget; public static void ThrowIfInvalid(T obj, Func messageFunc) where T : IValidationTarget; public static void ThrowIfInvalid(ValidationResult errors); public static void ThrowIfInvalid(ValidationResult errors, string message); } ``` -------------------------------- ### Handler with Automatic Validation Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/README.md Create a handler that inherits from Handler. The request object is guaranteed to be valid upon entering the HandleAsync method due to the registered ValidationBehavior. ```csharp public class CreateUserHandler : Handler { public override async ValueTask HandleAsync( CreateUserRequest request, CancellationToken cancellationToken) { // request is guaranteed to be valid here // ValidationBehavior has already validated it } } ``` -------------------------------- ### Custom Validator with Multiple TargetType Parameters Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/target-type-attribute.md Demonstrates a custom validator (BetweenAttribute) with multiple TargetType parameters, enabling it to work generically with different types for min and max bounds. ```csharp public sealed class BetweenAttribute( [TargetType] object minValue, [TargetType] object maxValue ) : ValidatorAttribute { public static bool ValidateProperty(T value, T min, T max) where T : IComparable { return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0; } public static string DefaultMessage => "'{PropertyName}' must be between {MinValueValue} and {MaxValueValue}."; } [Validate] public partial record RangedValue : IValidationTarget { [Between(0, 100)] public required int Percentage { get; init; } [Between(0.0, 1.0)] public required decimal Probability { get; init; } [Between("A", "Z")] public required string Letter { get; init; } } ``` -------------------------------- ### Register ValidationBehavior Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-behavior.md Register the ValidationBehavior in your assembly using the [Behaviors] attribute. This should be added to a file like Program.cs or AssemblyAttributes.cs. ```csharp using Immediate.Handlers.Shared; using Immediate.Validations.Shared; [assembly: Behaviors(typeof(ValidationBehavior<,>))] namespace MyApplication; ``` -------------------------------- ### HandleAsync Method Signature Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/validation-behavior.md The signature for the HandleAsync method within ValidationBehavior. This method is responsible for validating the request and proceeding with the handler pipeline. ```csharp public override async ValueTask HandleAsync( TRequest request, CancellationToken cancellationToken) ``` -------------------------------- ### IValidationTarget Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/types.md Represents an instance that can validate itself. Used for instance-based validation patterns. Manually implemented on validatable classes for instance method validation. ```APIDOC ## Interface IValidationTarget ### Description Represents an instance that can validate itself. Used for instance-based validation patterns. ### Methods - `ValidationResult Validate()`: Validates the instance. - `ValidationResult Validate(ValidationResult errors)`: Validates the instance and adds errors to the provided ValidationResult. ### Usage Manually implemented on validatable classes for instance method validation. ``` -------------------------------- ### Validate Instance Directly Source: https://github.com/immediateplatform/immediate.validations/blob/main/readme.md Validate an instance of an IV object by calling ClassName.Validate(instance). The results object contains validation information. ```csharp public void Method(Query query) { var results = Query.Validate(query); } ``` -------------------------------- ### Custom Validator with TargetType Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/target-type-attribute.md Illustrates creating a custom validator (MinimumValueAttribute) that uses the TargetType attribute on a constructor parameter to enable generic usage across different property types. ```csharp using Immediate.Validations.Shared; public sealed class MinimumValueAttribute( [TargetType] object minimumValue // Cast to property type ) : ValidatorAttribute { public static bool ValidateProperty(T value, T minimum) where T : IComparable { return value.CompareTo(minimum) >= 0; } public static string DefaultMessage => "'{PropertyName}' must be at least {MinimumValueValue}."; } // Now works for multiple types: [Validate] public partial record Range : IValidationTarget { [MinimumValue(18)] public required int Age { get; init; } [MinimumValue(100.50)] public required decimal Salary { get; init; } [MinimumValue("A")] public required string Category { get; init; } } ``` -------------------------------- ### Direct Validation Usage Source: https://github.com/immediateplatform/immediate.validations/blob/main/_autodocs/api-reference/ivalidation-target.md Perform direct validation on an object by calling the static Validate method. Use ValidationException.ThrowIfInvalid to immediately throw an exception if the validation fails. ```csharp var request = new CreateUserRequest { ... }; var result = CreateUserRequest.Validate(request); ValidationException.ThrowIfInvalid(result); ```