Architecture
Sannr's architecture is a fundamental shift in how .NET component validation is designed and executed. Instead of leaning heavily on System.Reflection at runtime, Sannr executes its validation discovery and rule construction process at compile-time.
The Problem With Reflection
Standard .NET validation leverages System.ComponentModel.DataAnnotations or robust libraries like FluentValidation. To remain loosely coupled and dynamic, these tools wait until your application starts upor often, until the very first validation requestto begin inspecting your [Required] or [MaxLength] attributes.
This reflection-first architecture incurs serious penalties:
- Slow App Startup: Hundreds of models undergo reflection inspection across multiple request threads.
- High Memory Overhead: Extracted attributes, parsed rules, and compiled expressions stay resident in memory (high GEN2 heap usage).
- Execution Sluggishness: Dynamic expression trees, boxed parameters, and
MethodInfo.Invokecalls burn clock cycles during every HTTP request. - Native AOT Roadblocks: Reflection-heavy tools struggle with the IL Trimmer. They require special directives or
[RequiresUnreferencedCode], which bloats binary sizes and undermines Native AOT guarantees.
The Solution: AOT Compilers as Validators
Sannr relies exclusively on Roslyn Source Generators. Because your C# code structure is totally known at compile-time, we shift the burden of validation discovery and construction entirely onto the MSBuild pipeline.
![]()
When you run dotnet build:
- The Sannr Code Analyzer intercepts the compilation graph.
- It detects classes adorned with
[Required],[Range], and custom validation traits. - Automatically generates an optimized, strongly-typed partial class to evaluate those properties and bounds entirely in clean static
C#logic. - Directly binds the validation method as a
Task<ValidationResult>executor.
Sample Generated Code
[SannrReflect]
public partial class RegisterDto
{
[Required, StringLength(10)]
public string Name { get; set; }
}Translates under-the-hood to hardcoded, zero-allocation condition trees:
// System generated AOT-safe validation
public static ValidationResult Validate(RegisterDto model)
{
var errors = new ValidationResult();
// No reflection, fast string lookups. No memory boxing.
if (string.IsNullOrWhiteSpace(model.Name))
{
errors.Add("Name", "Name is required.");
}
else if (model.Name.Length > 10)
{
errors.Add("Name", "Length cannot exceed 10 chars.");
}
return errors;
}This ensures zero allocations for metadata lookups and guarantees maximum throughput for every single incoming web request.