عنوان:

‫مدیریت وضعیت (State Management) در Blazor


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۳/۲۷ ۱۰:۱۰
آدرس: www.dntips.ir
چکیده (TL;DR): آیا با چالش‌هایی مانند از دست رفتن داده‌ها پس از رفرش صفحه، ارتباطات پیچیده و درهم‌تنیده میان کامپوننت‌ها، یا نشت غیرمنتظره داده‌ها (State Leaks) در اپلیکیشن Blazor خود دست‌وپنجه نرم می‌کنید؟ مشکل معمولاً از خود Blazor نیست، بلکه به نحوه مدیریت وضعیت (State) مربوط می‌شود. این راهنما به بررسی عمیق و کاربردی مؤثرترین الگوهای مدیریت وضعیت در Blazor — از وضعیت محلی کامپوننت و تزریق وابستگی (DI) گرفته تا حافظه مرورگر و پایداری داده‌ها در زمان Pre-rendering — می‌پردازد تا بتوانید برنامه‌هایی مقیاس‌پذیر، قابل اعتماد و با رفتار پیش‌بینی‌پذیر توسعه دهید.

مقدمه
تصور کنید اپلیکیشن Blazor شما به خوبی کار می‌کند، تا اینکه ارائه‌ی یک رفتار غیرمنتظره همه چیز را خراب می‌کند: کاربر صفحه را رفرش می‌کند و تمام داده‌های فرم ناپدید می‌شوند؛ یک تغییر کوچک در یک کامپوننت، وضعیت کامپوننت‌های دیگر را مختل می‌کند؛ یا در بدترین سناریوی ممکن، یک کاربر داده‌های حساس کاربر دیگری را مشاهده می‌کند!
این چالش‌ها در دنیای واقعی Blazor نادر نیستند، بلکه از رایج‌ترین مشکلاتی هستند که در صورت عدم اتخاذ یک استراتژی مشخص، پایداری برنامه را به شدت کاهش می‌دهند. واقعیت این است که برنامه شما خراب نیست، بلکه معماری مدیریت وضعیت آن نیاز به بازنگری دارد. Blazor انعطاف‌پذیری بالایی در اختیار شما قرار می‌دهد، اما اگر مرزهای مشخصی برای محل نگهداری و طول عمر وضعیت (State) تعیین نکنید، این انعطاف‌پذیری به آشفتگی تبدیل خواهد شد. با انتخاب الگوی درست، از شر چالش‌هایی مانند پراکندگی داده‌ها (Prop Drilling)، باگ‌های هم‌پوشانی داده‌های کاربران و سردرگمی در ردیابی تغییرات خلاص خواهید شد.
در این مقاله، الگوهای عملی مدیریت وضعیت در Blazor را همراه با نمونه کدهای ساده و بهینه بررسی می‌کنیم تا برنامه‌های شما پایدار، پیش‌بینی‌پذیر و آماده‌ی مقیاس‌پذیری باشند.

مدیریت وضعیت (State Management) چیست؟
پروتکل وب ذاتاً بدون وضعیت (Stateless) است. در یک اپلیکیشن وب، هر داده‌ای که به طور فعال و آگاهانه ذخیره نشود، با وقوع رویدادهای زیر از بین خواهد رفت:
  • رفرش کردن صفحه توسط کاربر (F5) یا بستن تب مرورگر.
  • ناوبری (Navigation) به یک صفحه دیگر.
  • قطع و وصل شدن کانال ارتباطی SignalR (در Blazor Server).
  • سوئیچ کردن اپلیکیشن از حالت پیش‌رندر شده (Prerendered HTML) به حالت تعاملی (Interactive Mode) در معماری جدید Blazor Web App.
بنابراین، مدیریت وضعیت یعنی تصمیم‌گیری مهندسی‌شده در این باره که داده‌ها کجا و با چه طول عمری (Lifetime) ذخیره شوند تا در مواجهه با رویدادهای فوق حفظ شده و در عین حال، به شکلی ایمن ایزوله بمانند و بین کاربران مختلف نشت نکنند.

مکانیسم مدیریت وضعیت در مدل‌های مختلف میزبانی Blazor
اگرچه Blazor مدل کامپوننت‌محور یکسانی را در معماری‌های مختلف ارائه می‌دهد، اما محل فیزیکی ذخیره‌سازی و طول عمر وضعیت در هر مدل به شدت متفاوت است:

۱. Blazor WebAssembly (اجرای کامل سمت کلاینت)
در این مدل، تمام ران‌تایم .NET و کد اپلیکیشن درون مرورگر کاربر (واسطه WebAssembly) اجرا می‌شوند. بنابراین وضعیت کاملاً در حافظه RAM مرورگر کلاینت قرار دارد:
  • مقادیر فیلدها و پراپرتی‌ها در رفرنس‌های کامپوننت.
  • ساختار درختی کامپوننت‌ها و آخرین خروجی رندر شده (Render Tree).
  • سرویس‌های ثبت شده در کانتینر DI (که در این مدل معمولاً به صورت Singleton رفتار می‌کنند، چرا که کل برنامه محدود به یک تب مرورگر است).
  • داده‌های ذخیره شده از طریق جاوااسکریپت (JS Interop) در حافظه مرورگر (localStorage یا sessionStorage).

۲. Blazor Server (اجرای سمت سرور با SignalR)
در این معماری، کامپوننت‌ها و منطق برنامه روی سرور اجرا شده و تغییرات UI از طریق یک اتصال زنده تحت وب‌سوکت (SignalR Circuit) به کلاینت فرستاده می‌شوند. وضعیت در اینجا به طول عمر این Circuit روی سرور وابسته است:
  • نمونه‌های کامپوننت و فیلدهای درون حافظه آن‌ها روی سرور ایجاد و نگهداری می‌شوند.
  • سرویس‌های DI با طول عمر Scoped به ازای هر اتصال (Circuit) کاربر زنده می‌مانند.

۳. Blazor Web App (مدل یکپارچه .NET 8/9/10)
این مدل ترکیبی از رندرینگ ایستا سمت سرور (Static SSR) و حالت‌های تعاملی (Interactive Server یا WebAssembly) است. چالش اصلی در اینجا، انتقال روان وضعیت از مرحله پیش‌رندر (Prerendering) روی سرور به مرحله تعاملی روی کلاینت است. برای این منظور از قابلیت‌هایی مانند PersistentComponentState و ویژگی جدید ویژگی‌های دکلراتیو برای هیدراته کردن (Hydration) خودکار داده‌ها استفاده می‌شود.

انتخاب الگوی مناسب بر اساس نیازمندی‌ها
برای یک انتخاب هوشمندانه، می‌توانید از چک‌لیست معمارانه زیر استفاده کنید:
  • وضعیت‌های قابل اشتراک‌گذاری یا بوکمارک شدن (مانند فیلترها): پارامترهای آدرس (URL Route / Query Parameters)
  • وضعیت‌های صرفاً گرافیکی و محدود به یک کامپوننت: فیلدهای محلی کُد (@code)
  • ارتباط مستقیم و گام‌به‌گام والد و فرزند: خواص [Parameter] به همراه EventCallback
  • اشتراک داده در لایه‌های عمیق یک درختواره: مقادیر آبشاری (CascadingValue / CascadingParameter)
  • ارتباط میان کامپوننت‌های غیرهمزاد یا صفحات مجزا: سرویس‌های تزریق وابستگی (DI Services)
  • وضعیت‌های اشتراکی واکنش‌گرا (Reactive): کانتینرهای وضعیت مجهز به رویداد اعلان تغییرات (State Containers)
  • حفظ پیوستگی داده از پیش‌رندر به حالت تعاملی: ویژگی [PersistentState] یا سرویس PersistentComponentState
  • ذخیره‌سازی امن و ماندگار در مرورگر (فقط سمت سرور): سرویس‌های ProtectedLocalStorage یا ProtectedSessionStorage

بررسی عمیق الگوهای مدیریت وضعیت همراه با کد

۱. پارامترهای URL و Query String
بهترین انتخاب برای حالاتی است که وضعیت باید با کپی کردن لینک (Bookmark) حفظ شود یا مستقیماً روی کنترل خروجی صفحه تأثیر دارد (مانند صفحه‌بندی یا سرچ).
نمونه پیاده‌سازی (.NET 10):
@page "/products/{Category}"
@inject NavigationManager NavManager

<h3>دسته بندی: @Category</h3>
<p>عبارت جستجو: @SearchTerm</p>

<input @bind="tempSearch" placeholder="جستجو..." />
<button @onclick="ApplySearch">اعمال فیلتر</button>

@code {
    [Parameter] public string? Category { get; set; }

    // اتصال خودکار به Query String با قابلیت تعامل کلاینت و سرور
    [SupplyParameterFromQuery(Name = "searchTerm")]
    public string? SearchTerm { get; set; }

    private string? tempSearch;

    protected override void OnParametersSet()
    {
        tempSearch = SearchTerm;
    }

    private void ApplySearch()
    {
        // بازسازی آدرس برای ثبت در تاریخچه مرورگر بدون از دست رفتن وضعیت
        var targetUri = NavManager.GetUriWithQueryParameter("searchTerm", tempSearch);
        NavManager.NavigateTo(targetUri);
    }
}
مزیت: هماهنگی کامل با دکمه‌های Back/Forward مرورگر و سئو (SEO) عالی در حالت SSR.
محدودیت: صرفاً برای داده‌های ساده (رشته، عدد و تاریخ) مناسب است و برای اشیاء پیچیده کاربرد ندارد.

۲. وضعیت محلی کامپوننت (Local Component State)
ساده‌ترین شکل مدیریت وضعیت است. هر فیلد یا پراپرتی خصوصی در بلوک @code یک وضعیت محلی است که کپسوله‌سازی کامل را فراهم می‌کند.
<button class="btn btn-primary" @onclick="ToggleDetails">
    @(isExpanded ? "پنهان‌سازی جزئیات" : "نمایش جزئیات")
</button>

@if (isExpanded)
{
    <div class="alert alert-info mt-2">این یک وضعیت کاملاً محلی است.</div>
}

@code {
    private bool isExpanded; // State محلی

    private void ToggleDetails() => isExpanded = !isExpanded;
}
نکته ارتقاء: اگر نیاز شد این وضعیت با کامپوننت همزاد (Sibling) به اشتراک گذاشته شود، باید وضعیت را به یک سطح بالاتر (والد مشترک) منتقل کنید (State Lifting).

۳. ارتباط والد-فرزند (Parameters & EventCallbacks)
رویکرد استاندارد برای جریان داده در یک ساختار درختی مستقیم. والد داده را با [Parameter] پایین می‌فرستد و فرزند با EventCallback رویدادها را بالا می‌فرستد.
<ChildComponent Title="وضعیت فعلی شمارنده" 
                CurrentValue="parentCounter" 
                OnIncrement="HandleIncrement" />

@code {
    private int parentCounter = 10;
    private void HandleIncrement(int step) => parentCounter += step;
}

<div class="card p-3">
    <h5>@Title</h5>
    <p>مقدار: @CurrentValue</p>
    <button @onclick="() => OnIncrement.InvokeAsync(1)">افزایش در والد</button>
</div>

@code {
    [Parameter] public string Title { get; set; } = string.Empty;
    [Parameter] public int CurrentValue { get; set; }
    [Parameter] public EventCallback<int> OnIncrement { get; set; }
}
نکته معماری: اگر تعداد لایه‌های میانی درختواره زیاد شود، این الگو منجر به پدیده‌ی کثیف Prop Drilling (پاس دادن اجباری پارامترها از لایه‌هایی که خودشان به آن نیاز ندارند) می‌شود. در چنین شرایطی باید به سراغ الگوی بعدی رفت.

۴. تزریق مقادیر در عمق درختواره (Cascading Parameters)
برای به اشتراک‌گذاری داده‌های سراسری زیرشاخه (مانند تم گرافیکی، اطلاعات هویتی کاربر یا تنظیمات عمومی تکست) بدون نیاز به پاس دادن لایه به لایه پارامترها استفاده می‌شود. در .NET 10 توصیه می‌شود برای بهینه‌سازی رندرینگ و مدیریت رویدادها از کلاس مدرن CascadingValueSource استفاده کنید.
<CascadingValue Value="currentTheme">
    <div class="@currentTheme">
        @Body
    </div>
</CascadingValue>

@code {
    private string currentTheme = "dark-theme";
}

@code {
    [CascadingParameter] public string? ActiveTheme { get; set; }
}

۵. مدیریت وضعیت سراسری با سرویس‌های DI (State Containers)
زمانی که کامپوننت‌های غیرهمزاد یا صفحات مختلف (مثلاً سبد خرید در صفحه محصولات و هدر سایت) نیاز به یک دیتای مشترک دارند، بهترین کار استفاده از یک کلاس ساده‌ی C# (اصطلاحاً State Container) است که در کانتینر DI ثبت می‌شود.

الف) مدیریت طول عمر (Lifetime Scopes) در سرویس‌ها:
انتخاب طول عمر مناسب حیاتی است، در غیر این صورت با نشت حافظه یا اشتراک ناخواسته دیتای کاربران مواجه می‌شوید:
  • AddScoped: در Blazor Server به ازای هر اتصال (هر کاربر/تب) یک نمونه مجزا می‌سازد (مناسب برای سبد خرید). در Blazor WebAssembly معادل کلاینت رفتار کرده و به ازای هر تب مرورگر تفکیک می‌شود.
  • AddSingleton: در Blazor Server خطرناک است؛ چون یک نمونه واحد برای تمام کاربران سیستم به اشتراک گذاشته می‌شود (نشت شدید داده!). اما در Blazor WebAssembly امن است چون کل ران‌تایم ایزوله در مرورگر یک فرد است.

ب) پیاده‌سازی یک کانتینر وضعیت واکنش‌گرا (Reactive State Container):
کلاس‌های معمولی DI در صورت تغییر پراپرتی، به کامپوننت‌ها اعلان رندر مجدد نمی‌فرستند. برای حل این مشکل، رویدادها را به کانتینر اضافه می‌کنیم:
// CounterStateContainer.cs
public class CounterStateContainer
{
    private int _count;

    public int Count
    {
        get => _count;
        set
        {
            if (_count != value)
            {
                _count = value;
                NotifyStateChanged();
            }
        }
    }

    // رویدادی که تغییر وضعیت را به گوش شنوندگان می‌رساند
    public event Action? OnChange;

    private void NotifyStateChanged() => OnChange?.Invoke();
}
نحوه مصرف در کامپوننت والد (نکته مهم: حتماً لغو اشتراک کنید):
@page "/reactive-demo"
@implements IDisposable
@inject CounterStateContainer StateContainer

<h3>مدیریت وضعیت واکنش‌گرا</h3>
<p>مقدار فعلی: <strong>@StateContainer.Count</strong></p>
<button class="btn btn-success" @onclick="Increment">افزایش وضعیت</button>

<div class="mt-3">
    <NestedChildComponent />
</div>

@code {
    protected override void OnInitialized()
    {
        // subscribe به رویداد تغییر برای به‌روزرسانی UI کامپوننت
        StateContainer.OnChange += StateHasChanged;
    }

    private void Increment() => StateContainer.Count++;

    public void Dispose()
    {
        // جلوگیری از نشت حافظه (Memory Leak) با لغو اشتراک
        StateContainer.OnChange -= StateHasChanged;
    }
}

۶. پایداری وضعیت پیش‌رندر (Prerendered State Persistence)
در Blazor Web App یا حالت‌های Pre-rendering، کامپوننت ابتدا یک‌بار روی سرور رندر شده تا خروجی HTML ایستا بسازد (برای سرعت لود اولیه بالاتر)، سپس بار دیگر روی کلاینت (WASM یا SignalR) هیدراته و تعاملی می‌شود. این رفتار دوگانه باعث می‌شود متد OnInitializedAsync دو بار اجرا شود که نتیجه آن فلیکر (Flicker) یا چشمک زدن ناخوشایند UI و اجرای دوباره کوئری‌های سنگین دیتابیس است.
در .NET 10، ویژگی دکلراتیو [PersistentState] این مشکل را به زیباترین شکل ممکن حل کرده است.
@page "/weather"
@rendermode InteractiveWebAssembly

<h3>پیش‌بینی آب و هوا</h3>

@if (forecasts == null)
{
    <p>در حال بارگذاری...</p>
}
else
{
    }

@code {
    // ران‌تایم دیتای این پراپرتی را در اولین رندر (سرور) سریالایز کرده
    // و در رندر دوم (کلاینت) بدون اجرای مجدد منطق متد، بازگردانی می‌کند.
    [PersistentState]
    public WeatherForecast[]? forecasts { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (forecasts == null)
        {
            // این بلاک فقط یک‌بار روی سرور اجرا می‌شود
            forecasts = await WeatherService.GetForecastsAsync();
        }
    }
}

کنترل پیشرفته رفتار بازگردانی داده باRestoreBehavior
در .NET 10 می‌توانید رفتار دقیق بازیابی وضعیت را کنترل کنید:
  • RestoreBehavior.SkipInitialValue: در رندر اولیه از حالت پیش‌رندر شده استفاده نمی‌کند.
  • RestoreBehavior.SkipLastSnapshot: وضعیت ذخیره شده قبلی را در زمان Reconnectهای ناگهانی نادیده گرفته و به روز رسانی جدید انجام می‌دهد.

۷. حافظه امن مرورگر (Protected Browser Storage)
برای داده‌هایی که باید در برابر رفرش‌های سخت (Hard Refresh) یا بستن مرورگر مقاومت کنند اما حاوی اطلاعات حساسی (مانند تنظیمات کاربری یا توکن‌های موقت غیر بحرانی) هستند، از ProtectedLocalStorage استفاده می‌شود. این سرویس داده‌ها را با استفاده از فرآیند ASP.NET Core Data Protection سمت سرور رمزنگاری کرده و سپس در مرورگر می‌نویسد.
@inject ProtectedLocalStorage ProtectedStorage

<button @onclick="SavePreferences">ذخیره تم کاربری</button>

@code {
    private async Task SavePreferences()
    {
        // داده‌ها قبل از ذخیره در لایه کلاینت به طور کامل و امن رمزنگاری می‌شوند
        await ProtectedStorage.SetAsync("UserTheme", "Dark-Fluid");
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // دسترسی به جاوااسکریپت و حافظه مرورگر فقط پس از رندر کلاینت ممکن است
            var result = await ProtectedStorage.GetAsync<string>("UserTheme");
            if (result.Success)
            {
                var theme = result.Value;
                // اعمال تم...
                StateHasChanged();
            }
        }
    }
}
قانون طلایی امنیت: هرگز توکن‌های JWT بسیار حساس یا اطلاعات هویتی کلیدی (PII) را بدون لایه‌های امنیتی تکمیلی درون localStorage کلاینت (به خصوص در WASM) ذخیره نکنید. حملات XSS می‌توانند دسترسی به حافظه عادی مرورگر را به مخاطره اندازند.

جدول مقایسه و جمع‌بندی الگوهای مدیریت وضعیت
ابزار مدیریت وضعیتبهترین سناریوی کاربردطول عمر و قلمرو (Scope)پایداری در برابر F5 (Refresh)
URL / Query Stringفیلترها، سرچ، صفحه‌بندی صفحاتوابسته به آدرس مرورگربله (صد در صد پایدار و قابل اشتراک)
Local Component Stateرفتارهای گرافیکی درون یک کامپوننتطول عمر نمونه کامپوننتخیر
Parameters & EventCallbackارتباطات مستقیم بالا به پایینمحدود به لایه کامپوننت والد-فرزندخیر
Cascading Valuesتنظیمات و ویژگی‌های ساختاری سراسری درختوارهزیردرخت مشخص شدهخیر
DI Services (Scoped)داده‌های مشترک صفحات (مانند سبد خرید)طول عمر زنده بودن Session / Circuitخیر (در رفرش سرور پاک می‌شود)
[PersistentState]انتقال دیتا از پیش‌رندر به حالت کلاینتبازه زمانی Hydration اولیهخیر (مختص جابجایی حالت رندر)
Protected Browser Storageترجیحات کاربر، تنظیمات امن هیدرتیشنحافظه ماندگار مرورگر (رمزگذاری شده)بله (کاملاً ماندگار)
ملاحظات عملکردی (Performance) و امنیتی

۱. مدیریت Render Tree و ترافیک پردازنده: فراخوانی بی‌رویه متد StateHasChanged() به خصوص در الگوهای واکنش‌گرا (State Containers)، می‌تواند کل زیردرخت کامپوننت‌ها را وادار به رندر مجدد کند. این امر در برنامه‌های بزرگ باعث افزایش شدید مصرف پردازنده (CPU Spike) می‌شود. برای جلوگیری از این مشکل، تغییر وضعیت‌ها را بهینه‌سازی کرده و متدها را به صورت هوشمند و تنها در صورت تغییر واقعی مقادیر صدا بزنید.

۲. نخ‌کشی و رویدادهای ناهمگام: هر زمان که به روز رسانی وضعیت از طریق رویدادهای خارجی نظیر تردها، تایمرها یا کانال‌های SignalR بک‌اند رخ می‌دهد، کُد به‌روزرسانی را حتماً در متد InvokeAsync کپسوله کنید:
await InvokeAsync(StateHasChanged);

۳. کلیدهای حفاظت از داده (Data Protection API): سرویس‌های ProtectedBrowserStorage از کلیدهای رمزنگاری سرور استفاده می‌کنند. در صورت ری‌استارت شدن سرور یا توزیع برنامه روی کلاسترهای چندگانه (Web Farm)، اگر کلیدها را روی یک دیسک یا سیستم مشترک (مانند Redis یا Azure Key Vault) ماندگار نکرده باشید، کلیدها منقضی شده و داده‌های ذخیره شده در مرورگر کاربران دیگر قابل رمزگشایی نخواهند بود.

نتیجه‌گیری
Blazor ابزاری فوق‌العاده منعطف و قدرتمند برای توسعه اپلیکیشن‌های مدرن سازمانی است، اما مدیریت وضعیت در آن نیازمند درک دقیق طول عمر داده‌ها و رفتار لایه‌های مختلف سرور و کلاینت است. برای داده‌های ساده و فیلترها از URL استفاده کنید، برای روابط مستقیم کامپوننت‌ها از الگوهای والد-فرزند، برای داده‌های مشترک صفحات از Scoped DI Containers مجهز به رویداد، و در نهایت برای مهاجرت بی‌نقص از سرور به کلاینت بدون افکت چشمک‌زدن، از ویژگی جدید و کارآمد [PersistentState] بهره ببرید.

نظرات

  • وحید نصیری در ۱۴۰۵/۰۳/۲۷ ۱۰:۲۰
    بخش تکمیلی: تحول مدیریت وضعیت در Blazor با انتشار NET 11.

    با انتشار .NET 11، مایکروسافت تمرکز ویژه‌ای روی بهبود تجربه توسعه در حالت Static SSR (رندر ایستا سمت سرور) گذاشته است. تا پیش از این، مدیریت وضعیت‌های کوتاه‌مدت یا فرار (Ephemeral State) در معماری Static SSR چالش‌برانگیز بود و توسعه‌دهندگان مجبور بودند به کوکی‌های سفارشی یا کدهای پیچیده متوسل شوند.
    در .NET 11، سه قابلیت کلیدی و دکلراتیو معرفی شده‌اند که مدیریت وضعیت را در الگوهایی مانند POST-Redirect-GET و فرم‌های چندمرحله‌ای به شدت ساده و استاندارد می‌کنند. در ادامه به بررسی این ویژگی‌ها می‌پردازیم.

    ۱. پشتیبانی بومی از TempData در Blazor SSR
    مکانیزم TempData که سال‌ها در ASP.NET Core MVC و Razor Pages ابزار محبوب توسعه‌دهندگان برای ذخیره‌سازی داده‌های موقت (با طول عمر تا درخواست HTTP بعدی) بود، اکنون به طور بومی به Blazor SSR اضافه شده است. این قابلیت برای سناریوهایی مانند نمایش پیام‌های موفقیت‌آمیز (Flash Messages) پس از ثبت فرم و انتقال داده در زمان ریدایرکت، فوق‌العاده کاربردی است.
    ویژگی‌های کلیدی:
    • ثبت خودکار: با فراخوانی متد AddRazorComponents در فایل Program.cs، مکانیزم TempData به صورت خودکار ثبت شده و به عنوان یک Cascading Value در دسترس کامپوننت‌ها قرار می‌گیرد.
    • امنیت بالا: ارائه‌دهنده پیش‌فرض کوکی (Cookie Provider) از سیستم ASP.NET Core Data Protection برای رمزنگاری داده‌ها استفاده می‌کند.
    • مدیریت طول عمر هوشمند: متد Get پس از خواندن مقدار، آن را از حافظه پاک می‌کند (استفاده یک‌باره). واسط ITempData متدهای Peek و Keep را برای کنترل دقیق‌تر طول عمر داده ارائه می‌دهد.

    نمونه کُد پیاده‌سازی:
    @page "/my-form"
    @inject NavigationManager NavigationManager
    
    <p class="alert alert-info">@_message</p>
    
    <form @onsubmit="HandleSubmit" method="post">
        <button type="submit" class="btn btn-primary">ثبت فرم</button>
    </form>
    
    @code {
        [CascadingParameter]
        public ITempData? TempData { get; set; }
    
        private string? _message;
    
        protected override void OnInitialized()
        {
            // خواندن مقدار و حذف خودکار آن از TempData برای درخواست‌های بعدی
            _message = TempData?.Get("Message") as string ?? "پیامی وجود ندارد";
        }
    
        private void HandleSubmit()
        {
            // ذخیره پیام موقت قبل از ریدایرکت
            TempData!["Message"] = "فرم با موفقیت ثبت شد!";
            NavigationManager.NavigateTo("/my-form", forceLoad: true);
        }
    }

    ۲. ویژگی دکلراتیو[SupplyParameterFromTempData]
    مایکروسافت برای حذف کدهای تکراری (Boilerplate) جهت خواندن و نوشتن در TempData، ویژگی دکلراتیو [SupplyParameterFromTempData] را معرفی کرده است. این ویژگی دقیقاً از همان الگوی آشنای [SupplyParameterFromQuery] پیروی می‌کند.

    نحوه کارکرد و کاربرد اصلی:
    زمانیکه این ویژگی را روی یک Property قرار می‌دهید، ران‌تایم به طور خودکار مقدار را از TempData می‌خواند و در آن Property تزریق می‌کند. نکته جذاب این است که مقداردهی به این Property، به طور خودکار آن را در TempData می‌نویسد تا در درخواست بعدی (بعد از ریدایرکت) قابل خواندن باشد.
    یک نمونه عینی از اهمیت این ویژگی، بازنویسی کامل قالب آماده Blazor Identity در .NET 11 است؛ مایکروسافت در این قالب، کوکی سفارشی مدیریت پیام‌های وضعیت (Status Message Cookie) را حذف و این ویژگی دکلراتیو را جایگزین آن کرده است.
    نمونه کُد پیاده‌سازی:
    @page "/account/manage"
    
    @if (!string.IsNullOrEmpty(StatusMessage))
    {
        <div class="alert alert-success">@StatusMessage</div>
    }
    
    @code {
        // اتصال خودکار دوطرفه به TempData
        [SupplyParameterFromTempData]
        public string? StatusMessage { get; set; }
    }
    نکته تخصصی:اگر نام Property با کلید ذخیره شده در TempData همخوانی ندارد، می‌توانید نام کلید دلخواه را از طریق پارامتر Name مشخص کنید: [SupplyParameterFromTempData(Name = "CustomKey")]

    ۳. ویژگی دکلراتیو[SupplyParameterFromSession]
    یکی دیگر از چالش‌های بزرگ در Blazor SSR، حفظ وضعیت‌های پیچیده‌تر مانند "سبد خرید" یا "مراحل طی شده در یک فرم چندمرحله‌ای (Wizard)" بدون فعال کردن حالت تعاملی (Interactive WebAssembly/Server) بود. در .NET 11، ویژگی [SupplyParameterFromSession] معرفی شده است تا ارتباط مستقیمی بین پراپرتی‌های کامپوننت و HTTP Session برقرار کند.

    نحوه کارکرد:
    داده‌ها با استفاده از System.Text.Json سریالایز شده و درست قبل از ارسال پاسخ HTTP به کلاینت، درون Session مربوط به کاربر بازنویسی می‌شوند.
    پیکربندی در فایل Program.cs:
    builder.Services.AddDistributedMemoryCache(); // نیاز به حافظه برای ذخیره سشن
    builder.Services.AddSession();
    builder.Services.AddRazorComponents();
    
    var app = builder.Build();
    
    app.UseSession(); // فعال‌سازی میان‌افزار سشن

    نمونه کُد پیاده‌سازی در کامپوننت:
    @page "/checkout"
    
    <div class="card p-4">
        <p>مرحله فعلی خرید: <strong>@CurrentStep</strong></p>
    
        <EditForm Model="Input" FormName="checkout" OnSubmit="NextStep">
            <button type="submit" class="btn btn-success">مرحله بعدی</button>
        </EditForm>
    </div>
    
    @code {
        // خواندن و نوشتن مستقیم و خودکار در HTTP Session کاربر
        [SupplyParameterFromSession(Name = "checkout-step")]
        public int CurrentStep { get; set; }
    
        private object Input { get; } = new();
    
        private void NextStep()
        {
            // با افزایش این مقدار، تغییرات به طور خودکار در سشن ذخیره می‌شوند
            CurrentStep++;
        }
    }

    جمع‌بندی ابزارهای جدید .NET 11

    جدول زیر دیدگاهی معمارانه برای انتخاب از میان ابزارهای جدید دات‌نت 11 به شما می‌دهد:
    ابزار جدیدمنبع ذخیره‌سازی اصلیطول عمر وضعیت (Lifetime)بهترین سناریوی مصرف
    ITempData (مستقیم)Cookie (رمزنگاری شده با Data Protection)فقط تا درخواست HTTP بعدی (حذف پس از خواندن)پیام‌های فلش سفارشی و مدیریت منطق‌های پیچیده حذف/حفظ داده
    [SupplyParameterFromTempData]Cookie / TempData Providerفقط تا درخواست HTTP بعدی (با مدیریت خودکار)پیام‌های وضعیت سیستم، هشدارها پس از عملیات ریدایرکت (POST-Redirect-GET)
    [SupplyParameterFromSession]Server Session (Memory/Redis)تا زمانی که Session کاربر زنده است (ماندگاری طولانی‌تر)سبد خرید در حالت SSR، فرم‌های چندمرحله‌ای پیچیده، حفظ فیلترهای پیشرفته کاربر
    یک نکته معماری:
    در پلتفرم‌های توزیع‌شده و بزرگ (Web Farms)، به یاد داشته باشید که ارائه‌دهنده پیش‌فرض TempData (کوکی‌محور) حجم کوکی‌های ارسالی را افزایش می‌دهد (محدودیت ۴ کیلوبایت مرورگر). همچنین سرویس Session به صورت پیش‌فرض از DistributedMemoryCache استفاده می‌کند که دیتای سشن را در حافظه همان سرور نگه می‌دارد. اگر برنامه‌ی Blazor SSR خود را روی چند سرور یا داکر پاد (Pod) مختلف مستقر می‌کنید، حتماً از SessionStorageTempDataProvider یا یک کش توزیع‌شده مانند Redis استفاده کنید تا کاربران با پدیده از دست رفتن سشن در اثر جابجایی بین سرورها مواجه نشوند.