عنوان:

‫پایان یک انتظار طولانی: اعتبارسنجی ناهمگام (Async Validation) در NET 11.


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۴/۱۴ ۰۸:۴۵
آدرس: www.dntips.ir
در طول سال‌های گذشته، سیستم اعتبارسنجی مدل (Model Validation) توکار در اکوسیستم دات‌نت که عمدتاً بر پایه‌ی فضای نام System.ComponentModel.DataAnnotations استوار است، ساختاری کاملاً همگام (Synchronous) داشت. این طراحی سنتی، پیاده‌سازی قواعد اعتبارسنجی پیچیده‌ای را که به عملیات‌های ورودی/خروجی (I/O-bound) وابسته بودند با چالش مواجه می‌کرد؛ سناریوهایی مانند بررسی منحصربه‌فرد بودن نام کاربری در پایگاه داده، استعلام وضعیت یک شناسه از طریق Web API خارجی یا سنجش موجودی انبار.
توسعه‌دهندگان برای پیاده‌سازی این زیرساخت‌ها ناچار بودند یا به ابزارهای شخص ثالث مانند کتابخانه محبوب FluentValidation روی آورند، یا متدهای ناهمگام را به صورت ناامن و مسدودکننده (Thread Blocking) با استفاده از امضا‌هایی مانند .Result یا .Wait() فراخوانی کنند. این رویکرد نامناسب، مستقیماً ریسک بروز گلوگاه در پایگاه ریسمان‌ها (Thread Pool Starvation) را افزایش می‌داد. در پی درخواست‌های مکرر جامعه توسعه‌دهندگان، مایکروسافت بالاخره در .NET 11 Preview 6 زیرساخت اعتبارسنجی ناهمگام را به صورت بومی به هسته فریم‌ورک اضافه کرد تا به یکی از پررای‌ترین درخواست‌های گیت‌هاب (Issue #31905) پاسخ دهد.

معماری و قابلیت‌های جدید در .NET 11
این تغییرات زیرساختی، معماری اعتبارسنجی دات‌نت را متحول کرده است. در این مکانیزم جدید، سه افزونه اساسی به فضای نام System.ComponentModel.DataAnnotations اضافه شده است که به توسعه‌دهندگان اجازه می‌دهد منطق ناهمگام خود را بدون مسدود کردن ریسمان‌ها پیاده‌سازی کنند:
  • کلاس انتزاعی AsyncValidationAttribute: این کلاس پایه جدید، جایگزین مستقیم ValidationAttribute برای سناریوهای ناهمگام است و امکان نوشتن اتریبیوت‌های سفارشی را با بازنویسی (Override) متد IsValidAsync فراهم می‌کند.
  • اینترفیس IAsyncValidatableObject: همتای ناهمگام اینترفیس شناخته‌شده‌ی IValidatableObject که به خودِ مدل یا DTO اجازه می‌دهد منطق اعتبارسنجی درونی‌اش را به صورت اسنک (Async) پیاده‌سازی کند.
  • متدهای نوین در کلاس static اعتبارسنجی (Validator): معرفی متدهای جدید از جمله ValidateObjectAsync، TryValidateObjectAsync، ValidatePropertyAsync و ValidateValueAsync جهت اجرای غیرمسدودکننده عملیات سنجش صحت داده‌ها.

پیاده‌سازی عملی اتریبیوت اعتبارسنجی ناهمگام
در نمونه کد زیر، شیوه تعریف یک قانون اعتبارسنجی ناهمگام جهت بررسی منحصربه‌فرد بودن نام کاربری به ساده‌ترین شکل ممکن ارائه شده است. متد سنتی IsValid به صورت پیش‌فرض برای سازگاری عقب‌رو (Backward Compatibility) مقدار ValidationResult.Success را برمی‌گرداند و منطق اصلی در IsValidAsync به همراه پشتیبانی از CancellationToken پیاده‌سازی می‌شود.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;

public sealed class UniqueUserNameAttribute : AsyncValidationAttribute
{
    // سازگاری با خط لوله‌های همگام قدیمی
    protected override ValidationResult? IsValid(object? value, ValidationContext context)
        => ValidationResult.Success;

    // پیاده‌سازی اصلی برای خط لوله ناهمگام دات‌نت ۱۱
    protected override async Task<ValidationResult?> IsValidAsync(
        object? value, ValidationContext context, CancellationToken cancellationToken)
    {
        // دریافت مستقیم سرویس‌ها از کانتینر DI با استفاده از Context موجود
        var users = context.GetRequiredService<IUserStore>();
        
        bool isTaken = await users.ExistsAsync((string?)value, cancellationToken);
        
        return isTaken
            ? new ValidationResult("این نام کاربری قبلاً توسط کاربر دیگری انتخاب شده است.")
            : ValidationResult.Success;
    }
}

public class RegistrationRequest
{
    [Required]
    [UniqueUserName]
    public string UserName { get; set; } = "";
}
جهت فراخوانی و اعتبارسنجی ناهمگام این مدل در خط لوله‌های دستی یا خارج از لایه کنترلر، موتور Validator متدهای الحاقی جدید خود را ارائه می‌دهد:
// اجرای فرآیند اعتبارسنجی ناهمگام بدون بلاک کردن Thread
var context = new ValidationContext(model, serviceProvider, items: null);
await Validator.ValidateObjectAsync(model, context, validateAllProperties: true);
نکته: یکی از نکات کلیدی هنگام استفاده از ValidationContext.GetRequiredService در محیط‌های ناهمگام، طول عمر (Lifetime) سرویس تزریق‌شده است. اگر سرویس شما دارای طول عمر محدوده (Scoped) باشد (مانند DbContext در Entity Framework Core)، اطمینان حاصل کنید که نمونه کانتینیشن جاری در محدوده لایف‌تایم درخواست وب (HTTP Request Scope) اجرا می‌شود تا با خطای دسترسی همزمان (Concurrency) مواجه نشوید.

توسعه به زیرسیستم Configuration و اعتبارسنجی زمان راه‌اندازی (Startup)
تغییرات شگرف دات‌نت ۱۱ محدود به لایه‌ی مدل‌های ورودی (API Models) نیست؛ مایکروسافت این رویکرد را به سیستم مدیریت تنظیمات برنامه یعنی Microsoft.Extensions.Options نیز گسترش داده است.
از طریق اینترفیس جدید IAsyncStartupValidator، اپلیکیشن‌ها می‌توانند کدهای مربوط به تنظیمات وب و سرویس‌های ابری خود را که نیازمند اعتبارسنجی شبکه‌ای هستند (مانند بررسی صحت خط اتصال پایگاه داده، صحت کلیدهای API متصل به درگاه‌های مالی، یا در دسترس بودن سرور کش رادیس) در زمان بوت شدن برنامه به صورت ناهمگام بررسی کنند.
این مکانیزم پیاده‌سازی الگوی طراحی Fail-Fast را در بهینه‌ترین حالت ممکن میسر می‌سازد. اگر پیکربندی‌های حیاتی سیستم نادرست باشد، سرویس پیش از پردازش هرگونه درخواست ورودی و بدون معطل نگه داشتن ترد، با خطا متوقف (Crash) می‌شود تا از رفتارهای پیش‌بینی‌نشده در زمان اجرا جلوگیری شود.

نتیجه‌گیری
یکپارچه‌سازی سیستم ناهمگام با DataAnnotations در .NET 11 یکی از بنیادین‌ترین اصلاحات ساختاری در زیرسیستم اعتبارسنجی در چند سال گذشته است. این قابلیت با آزادسازی پتانسیل مسدودکننده کدهای قدیمی، مقیاس‌پذیری (Scalability) و نرخ پاسخ‌دهی سرور تحت بارهای ترافیکی شدید را به شکل چشمگیری ارتقا می‌دهد. با کاهش وابستگی به کتابخانه‌های فرعی و افزایش انعطاف‌پذیری Options Pattern در زمان استارت‌آپ، توسعه‌دهندگان دات‌نت اکنون ابزاری بومی، قدرتمند و فوق‌العاده بهینه برای مدیریت صحت داده‌ها در اختیار دارند.