Skip to content

ZibStack.NET.Validation

NuGet Source

Source-generated validation for .NET. Add validation attributes, get a compile-time Validate() method — no reflection, no runtime overhead.

Terminal window
dotnet add package ZibStack.NET.Validation
using ZibStack.NET.Validation;
[ZValidate]
public partial class CreateUserRequest
{
[ZRequired]
[ZMinLength(2)]
[ZMaxLength(50)]
public string Name { get; set; } = "";
[ZRequired]
[ZEmail]
public string Email { get; set; } = "";
[ZRange(18, 120)]
public int Age { get; set; }
[ZUrl]
public string? Website { get; set; }
}

The generator creates a Validate() method at compile-time:

var request = new CreateUserRequest { Name = "", Email = "bad", Age = 10 };
var result = request.Validate();
if (!result.IsValid)
{
foreach (var error in result.Errors)
Console.WriteLine(error);
// "Name is required."
// "Email must be a valid email address."
// "Age must be between 18 and 120."
}
AttributeTargetDescription
[ZValidate]Class/RecordMarks the type for validation generation (must be partial)
[ZRequired]PropertyMust not be null (or empty/whitespace for strings)
[ZMinLength(n)]PropertyString/collection must have at least n characters/items
[ZMaxLength(n)]PropertyString/collection must have at most n characters/items
[ZRange(min, max)]PropertyNumeric value must be within range (inclusive)
[ZEmail]PropertyMust be a valid email address
[ZUrl]PropertyMust be a valid absolute URL
[ZMatch(pattern)]PropertyMust match the regex pattern
[ZNotEmpty]PropertyCollection must have items; string must not be whitespace

All attributes support a Message property for custom error messages:

[ZRequired(Message = "Please provide your name")]
[ZMatch(@"^\d{3}-\d{4}$", Message = "Phone must be in format XXX-XXXX")]

All [ZValidate] types implement IValidatable:

public interface IValidatable
{
ValidationResult Validate();
}
// Use in generic code
void Process<T>(T request) where T : IValidatable
{
var result = request.Validate();
if (!result.IsValid)
throw new ArgumentException(string.Join(", ", result.Errors));
}
public sealed class ValidationResult
{
public IReadOnlyList<string> Errors { get; }
public bool IsValid { get; }
public static readonly ValidationResult Success; // singleton
}

Works with records too:

[ZValidate]
public partial record ProductRequest
{
[ZRequired]
public string Sku { get; init; } = "";
[ZRange(0, 999999)]
public decimal Price { get; init; }
}
[HttpPost]
public IActionResult Create(CreateUserRequest request)
{
var validation = request.Validate();
if (!validation.IsValid)
return BadRequest(new { Errors = validation.Errors });
// proceed...
}
  • .NET 8.0+
  • Types must be partial

MIT