SGuard.DataAnnotations
0.1.0
dotnet add package SGuard.DataAnnotations --version 0.1.0
NuGet\Install-Package SGuard.DataAnnotations -Version 0.1.0
<PackageReference Include="SGuard.DataAnnotations" Version="0.1.0" />
<PackageVersion Include="SGuard.DataAnnotations" Version="0.1.0" />
<PackageReference Include="SGuard.DataAnnotations" />
paket add SGuard.DataAnnotations --version 0.1.0
#r "nuget: SGuard.DataAnnotations, 0.1.0"
#:package SGuard.DataAnnotations@0.1.0
#addin nuget:?package=SGuard.DataAnnotations&version=0.1.0
#tool nuget:?package=SGuard.DataAnnotations&version=0.1.0
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.
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.