SGuard.DataAnnotations 0.1.0

dotnet add package SGuard.DataAnnotations --version 0.1.0
                    
NuGet\Install-Package SGuard.DataAnnotations -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SGuard.DataAnnotations" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SGuard.DataAnnotations" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="SGuard.DataAnnotations" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SGuard.DataAnnotations --version 0.1.0
                    
#r "nuget: SGuard.DataAnnotations, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SGuard.DataAnnotations@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SGuard.DataAnnotations&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=SGuard.DataAnnotations&version=0.1.0
                    
Install as a Cake Tool

SGuard.DataAnnotations

CI NuGet Version NuGet Downloads Coverage

SGuard.DataAnnotations provides localized and extensible DataAnnotations support for .NET, including:

  • Localizable validation attributes (with robust fallback and custom error handling)
  • Collection, conditional, and property comparison validators are not found in standard DataAnnotations
  • Guard pattern (Is.* for boolean return, ThrowIf.* for exception-throwing) for model validation
  • Seamless integration with DataAnnotations and SGuard's fail-fast/callback philosophy
  • Well-tested and extensible for real-world application scenarios

Note: For fluent validation/guard support, see the upcoming SGuard.FluentValidation package.

Important: If you want to implement custom callback, fail-fast, or chainable guard logic, you should also review the SGuard core project, which provides advanced guard and callback APIs used by this library.


Table of Contents


Installation

Install via NuGet:

dotnet add package SGuard.DataAnnotations

Features

  • Localized error messages via resource files (.resx), with fallback to default or custom messages.
  • Advanced collection and conditional validation (min/max count, required-if, required collection, collection element validation, etc.).
  • Comparison attributes for property-to-property or value-to-value checks (greater than, less than, between, compare, etc.).
  • Full DataAnnotations compatibility—use SGuard attributes anywhere a standard attribute is accepted.
  • Guard pattern API (Is/ThrowIf) for easy imperative validation and exception/callback handling.

Supported Languages

SGuard.DataAnnotations ships with built-in resource support for the following languages:

Language Culture Code Localized Resource File
English (default) en SGuardDataAnnotations.resx
Turkish tr SGuardDataAnnotations.tr.resx
German de SGuardDataAnnotations.de.resx
French fr SGuardDataAnnotations.fr.resx
Russian ru SGuardDataAnnotations.ru.resx
Japanese ja SGuardDataAnnotations.ja.resx
Hindi hi SGuardDataAnnotations.hi.resx

Note:

  • If the current UI culture is not found, SGuard will fallback to English or to the fallback message if provided.
  • You can add your own resource files to support additional languages.
  • How to add your own language?

Supported Attributes

String & Common Validators

Attribute Purpose Supported Types Example Usage
SGuardRequiredAttribute Required field (localized) Any [SGuardRequired(typeof(Resources.SGuardDataAnnotations), "Username_Required")]
SGuardMinLengthAttribute Minimum string length string, array, ICollection [SGuardMinLength(5, typeof(Resources.SGuardDataAnnotations), "Username_MinLength")]
SGuardMaxLengthAttribute Maximum string length string, array, ICollection [SGuardMaxLength(20, typeof(Resources.SGuardDataAnnotations), "Username_MaxLength")]
SGuardStringLengthAttribute Min/max string length string [SGuardStringLength(12, typeof(Resources.SGuardDataAnnotations), "Username_MaxLength")]
SGuardRegularExpressionAttribute Regex pattern string [SGuardRegularExpression("^[a-zA-Z0-9_]+$", typeof(Resources.SGuardDataAnnotations), "Username_InvalidCharacters")]
SGuardEmailAddressAttribute Email format string [SGuardEmailAddress(typeof(Resources.SGuardDataAnnotations), "Email_InvalidFormat")]
SGuardPhoneAttribute Phone format string [SGuardPhone(typeof(Resources.SGuardDataAnnotations), "Profile_Phone_Invalid")]
SGuardUrlAttribute URL format string [SGuardUrl(typeof(Resources.SGuardDataAnnotations), "Common_Url_Invalid")]
SGuardCreditCardAttribute Credit card format string [SGuardCreditCard(typeof(Resources.SGuardDataAnnotations), "Common_CreditCard_Invalid")]
SGuardRangeAttribute Value must be within a numeric range int, double [SGuardRange(1, 10, typeof(Resources.SGuardDataAnnotations), "Common_Range")]

Collection Validators

Attribute Purpose Supported Types Example Usage
SGuardRequiredCollectionAttribute Collection must not be null/empty IEnumerable, arrays, etc. [SGuardRequiredCollection(typeof(Resources.SGuardDataAnnotations), "Common_Collection_Required")]
SGuardMinCountAttribute Minimum item count in collection IEnumerable, arrays, etc. [SGuardMinCount(2, typeof(Resources.SGuardDataAnnotations), "Common_Collection_MinCount")]
SGuardMaxCountAttribute Maximum item count in collection IEnumerable, arrays, etc. [SGuardMaxCount(5, typeof(Resources.SGuardDataAnnotations), "Common_Collection_MaxCount")]
SGuardCollectionItemsMatchAttribute Each item must match one/more validators (e.g. regex, required) IEnumerable, arrays, etc. [SGuardCollectionItemsMatch(typeof(EmailAddressAttribute), typeof(Resources.SGuardDataAnnotations), "Email_InvalidFormat", AggregateAllErrors = true)]

Details:

  • SGuardCollectionItemsMatchAttribute can take multiple validators and will apply them to each item.
  • AggregateAllErrors (default: false): If true, collects all errors; if false, returns on first failure.
  • Supported on any IEnumerable (e.g., List<T>, arrays, custom collections).

Conditional Validators

Attribute Purpose Supported Types Example Usage
SGuardRequiredIfAttribute Field required if another property equals value Any [SGuardRequiredIf("Country", "USA", typeof(Resources.SGuardDataAnnotations), "Address_Required")]

Comparison Validators

Attribute Purpose Supported Types Example Usage
SGuardCompareAttribute Values must be equal (like CompareAttribute) Any [SGuardCompare("Password", typeof(Resources.SGuardDataAnnotations), "Password_Mismatch")]
SGuardBetweenAttribute Value must be between two properties IComparable types [SGuardBetween("Min", "Max", true, typeof(Resources.SGuardDataAnnotations), "Common_Between")]
SGuardGreaterThanAttribute Value must be greater than another property IComparable types [SGuardGreaterThan("MinAge", typeof(Resources.SGuardDataAnnotations), "Profile_BirthDate_MinimumAge")]
SGuardLessThanAttribute Value must be less than another property IComparable types [SGuardLessThan("MaxAge", typeof(Resources.SGuardDataAnnotations), "Profile_BirthDate_MaximumAge")]

Supported types: int, double, decimal, DateTime, string, etc. (anything implementing IComparable)


Guard Pattern API

SGuard.DataAnnotations provides two imperative APIs for runtime validation, following the SGuard pattern.

Is.* Methods

  • Purpose: Return bool for validation checks (never throw).

  • Callback: Optional SGuardCallback invoked with GuardOutcome.Success/Failure.
    For advanced callback usage, see the SGuard project documentation.

  • Example:

    if (Is.DataAnnotationsValid(model))
    {
        // model is valid
    }
    
  • With callback:

    bool valid = Is.DataAnnotationsValid(model, callback: outcome =>
    {
        if (outcome == GuardOutcome.Failure)
            Console.WriteLine("Validation failed!");
    });
    
  • Get all validation errors:

    bool valid = Is.DataAnnotationsValid(model, out var results);
    foreach (var err in results)
        Console.WriteLine($"{string.Join(", ", err.MemberNames)}: {err.ErrorMessage}");
    

ThrowIf.* Methods

  • Purpose: Throw exception if validation fails.

  • Callback: Invoked before throw (GuardOutcome.Failure) or on pass (GuardOutcome.Success).

  • Example:

    ThrowIf.DataAnnotationsInValid(model);
    // Throws DataAnnotationsException if model is invalid.
    
  • Custom exception:

    ThrowIf.DataAnnotationsInValid<ArgumentException>(model, new ArgumentException("Custom error!"));
    
  • Custom exception with constructor args:

    ThrowIf.DataAnnotationsInValid<ArgumentException>(model, new object[] { "Custom error!" });
    
Exception Details
  • Throws DataAnnotationsException by default, which contains all validation errors.
  • Extract errors (see also SGuard.DataAnnotations.Extensions):
    catch (DataAnnotationsException ex)
    {
        if (ex.TryGetValidationErrors(out var errors))
        {
            foreach (var err in errors)
                Console.WriteLine($"{string.Join(", ", err.Members)}: {err.Message}");
        }
    }
    

Localization & Fallback

  • All SGuard attributes support:
    • ErrorMessageResourceType and ErrorMessageResourceName (standard .NET resource workflow)
    • FallbackResourceName: Used if the main resource key is missing.
    • FallbackMessage: Used if both resource keys are missing.
  • Culture: Error messages are localized to the current UICulture.
  • Example:
    [SGuardMinLength(3, typeof(Resources.SGuardDataAnnotations), "Username_MinLength", 
        FallbackResourceName = "Common_MinLength", FallbackMessage = "Min length required.")]
    public string Username { get; set; }
    

How to add a custom language?

  1. Copy SGuardDataAnnotations.resx and rename to e.g. SGuardDataAnnotations.es.resx for Spanish.
  2. Translate all keys/values.
  3. Rebuild and set your thread/UI culture accordingly.

Extending SGuard.DataAnnotations

Want to add your own fully localized validation attribute?
Inherit from SGuardValidationAttributeBase and implement IsValid:

public class MyCustomLocalizedAttribute : SGuardValidationAttributeBase
{
    public MyCustomLocalizedAttribute(Type resourceType, string resourceName)
        : base(resourceType, resourceName) {}

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        // Your logic here
        if (value == null) 
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        return ValidationResult.Success;
    }
}

Minimal API Example (Real World)

Here's how you use SGuard.DataAnnotations in an ASP.NET Core Minimal API:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using SGuard.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/register", (UserRegistration model) =>
{
    if (!Is.DataAnnotationsValid(model, out var errors))
        return Results.BadRequest(errors.Select(e => new { e.MemberNames, e.ErrorMessage }));

    // If valid, continue
    return Results.Ok("Registration successful!");
});

app.Run();

public class UserRegistration
{
    [SGuardRequired(typeof(Resources.SGuardDataAnnotations), "Username_Required")]
    [SGuardMinLength(3, typeof(Resources.SGuardDataAnnotations), "Username_MinLength")]
    [SGuardMaxLength(20, typeof(Resources.SGuardDataAnnotations), "Username_MaxLength")]
    public string Username { get; set; }

    [SGuardRequired(typeof(Resources.SGuardDataAnnotations), "Email_Required")]
    [SGuardEmailAddress(typeof(Resources.SGuardDataAnnotations), "Email_InvalidFormat")]
    public string Email { get; set; }

    [SGuardRequired(typeof(Resources.SGuardDataAnnotations), "Password_Required")]
    [SGuardStringLength(100, typeof(Resources.SGuardDataAnnotations), "Password_MaxLength")]
    public string Password { get; set; }

    [SGuardCompare("Password", typeof(Resources.SGuardDataAnnotations), "Password_Mismatch")]
    public string ConfirmPassword { get; set; }

    [SGuardRequiredCollection(typeof(Resources.SGuardDataAnnotations), "Profile_Phone_Required")]
    [SGuardCollectionItemsMatch(typeof(SGuardPhoneAttribute), typeof(Resources.SGuardDataAnnotations), "Profile_Phone_Invalid", AggregateAllErrors = true)]
    public List<string> PhoneNumbers { get; set; }
}

Advanced Topics

Error Handling

  • Guard methods: Return bool or throw, never both.
  • Attributes: Always return ValidationResult, never throw.
  • All exceptions: Contain full error detail, member names, and support for extraction/extension.
  • For advanced fail-fast, callback, or custom guard usage:
    See SGuard project documentation.

FAQ / Tips

Q: Can I use SGuard attributes in ASP.NET Core, Blazor, WinForms, etc.?
A: Yes, SGuard attributes implement the standard DataAnnotations contract.

Q: What happens if a resource key is missing?
A: The attribute will use FallbackResourceName if provided, otherwise FallbackMessage, otherwise [ResourceKey].

Q: How do I validate a collection’s items?
A: Use [SGuardCollectionItemsMatch(...)]. See Collection Validators.

Q: Can I chain SGuard and standard DataAnnotations attributes?
A: Yes, you can stack any combination on your model.

Q: Will SGuard.DataAnnotations work with FluentValidation?
A: Yes, as long as you use DataAnnotations integration. For a full fluent API, see SGuard.FluentValidation.

Q: How do I quickly test everything?
A:

  1. Run all tests (requires .NET SDK):
    dotnet test
    
  2. For a quick validation in your app, call:
    if (!Is.DataAnnotationsValid(model, out var results))
        // handle errors, see 'results'
    

Contributing

Pull requests, issues, and suggestions are very welcome!
See CONTRIBUTING.md for guidelines.


License

MIT License. See LICENSE.


See Also


Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 29 9/9/2025

Initial release. Supports .NET 6, 7, 8, 9. Multilingual validation, guard clauses, and CI/CD integration.