SGuard.DataAnnotations
0.1.1
dotnet add package SGuard.DataAnnotations --version 0.1.1
NuGet\Install-Package SGuard.DataAnnotations -Version 0.1.1
<PackageReference Include="SGuard.DataAnnotations" Version="0.1.1" />
<PackageVersion Include="SGuard.DataAnnotations" Version="0.1.1" />
<PackageReference Include="SGuard.DataAnnotations" />
paket add SGuard.DataAnnotations --version 0.1.1
#r "nuget: SGuard.DataAnnotations, 0.1.1"
#:package SGuard.DataAnnotations@0.1.1
#addin nuget:?package=SGuard.DataAnnotations&version=0.1.1
#tool nuget:?package=SGuard.DataAnnotations&version=0.1.1
SGuard.DataAnnotations
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
- Features
- Supported Languages
- Supported Attributes
- Guard Pattern API
- Localization & Fallback
- Extending SGuard.DataAnnotations
- Minimal API Example (Real World)
- Advanced Topics
- FAQ / Tips
- Contributing
- License
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
): Iftrue
, collects all errors; iffalse
, 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 withGuardOutcome.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
andErrorMessageResourceName
(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?
- Copy
SGuardDataAnnotations.resx
and rename to e.g.SGuardDataAnnotations.es.resx
for Spanish. - Translate all keys/values.
- 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:
- Run all tests (requires .NET SDK):
dotnet test
- 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 | Versions 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. |
-
net6.0
- SGuard (>= 0.1.1)
- System.ComponentModel.Annotations (>= 5.0.0)
-
net7.0
- SGuard (>= 0.1.1)
- System.ComponentModel.Annotations (>= 5.0.0)
-
net8.0
- SGuard (>= 0.1.1)
- System.ComponentModel.Annotations (>= 5.0.0)
-
net9.0
- SGuard (>= 0.1.1)
- System.ComponentModel.Annotations (>= 5.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial release. Supports .NET 6, 7, 8, 9. Multilingual validation, guard clauses, and CI/CD integration.