ASP.NET Core MVC Mastery

TempData

1 Views Updated 5/4/2026

TempData in ASP.NET Core MVC — The Complete Production Guide

HTTP is stateless. Every request is a stranger. So how does your application remember a success notification after a form submission redirects the user? That's the exact problem TempData was built to solve — and understanding it deeply separates a junior developer from a production-ready engineer.

1. WHAT is TempData?

TempData is a short-lived, dictionary-based storage mechanism in ASP.NET Core MVC that allows you to pass data from one HTTP request to the very next request — and then it self-destructs. Think of it as a sticky note you hand to the next person in line, and once they read it, it dissolves.

Internally, TempData implements ITempDataDictionary, which inherits from IDictionary<string, object>. It stores values as object, meaning you must cast back to the original type when reading.

The Core Mental Model
  • ViewData / ViewBag → Live for the current request only (Controller → View)
  • TempData → Survives exactly ONE redirect (Controller → Redirect → Next Controller/View)
  • Session → Persists across many requests until timeout or explicit removal

2. WHY Do We Need TempData? The PRG Problem

The real-world reason TempData exists is the Post-Redirect-Get (PRG) Pattern. Consider this real production scenario:

  1. User fills out an order form and clicks Submit (POST request)
  2. Server processes the order successfully
  3. Server redirects user to /orders (GET request) — this prevents duplicate form submissions on refresh
  4. The orders page needs to show: "Order #4521 placed successfully!"

The Problem: Step 3 triggers a brand-new HTTP request. ViewData and ViewBag from Step 2 are completely gone — they don't survive across requests. You need a mechanism to pass that success message across the redirect boundary.

The Solution: TempData. It stores the data in a cookie (default) or session, and it survives precisely until it is read in the next request.

3. HOW TempData Works Under the Hood

ASP.NET Core provides two TempData Providers — the mechanism that physically stores and retrieves TempData between requests:

Cookie-Based Provider (Default)
  • TempData values are serialized to JSON, then encrypted using ASP.NET Core's Data Protection API
  • Stored as a cookie on the client's browser
  • No server-side state needed — perfect for web farms and load balancers
  • Limitation: Cookie size limit (~4KB). Don't store large objects
Session-Based Provider
  • Stores TempData on the server-side in session storage
  • Requires AddSession() and UseSession() middleware setup
  • Can handle larger payloads than cookies
  • Trade-off: Requires sticky sessions in web farms unless using distributed session (Redis)

Switching to Session-Based TempData Provider:

// Program.cs — Switching to Session-based TempData
builder.Services.AddControllersWithViews()
    .AddSessionStateTempDataProvider(); // Switch from cookie to session

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

var app = builder.Build();
app.UseSession(); // Must come BEFORE UseRouting
app.UseRouting();
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

4. REAL-TIME PRODUCTION EXAMPLES

Example 1: E-Commerce Order Notification (PRG Pattern)

// OrdersController.cs — Production-grade POST handler
public class OrdersController : Controller
{
    private readonly IOrderService _orderService;
    private readonly ILogger<OrdersController> _logger;

    public OrdersController(IOrderService orderService, ILogger<OrdersController> logger)
    {
        _orderService = orderService;
        _logger = logger;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> PlaceOrder(OrderViewModel model)
    {
        if (!ModelState.IsValid)
            return View("Checkout", model);

        try
        {
            var orderId = await _orderService.CreateOrderAsync(model);

            // Store notification message in TempData — survives the redirect
            TempData["Notification"] = $"Order #{orderId} placed successfully! 🎉";
            TempData["NotificationType"] = "success";

            _logger.LogInformation("Order {OrderId} created by user {UserId}", orderId, User.Identity.Name);

            return RedirectToAction(nameof(Confirmation), new { id = orderId });
        }
        catch (InsufficientStockException ex)
        {
            TempData["Notification"] = $"Item '{ex.ProductName}' is out of stock.";
            TempData["NotificationType"] = "danger";
            return RedirectToAction(nameof(Index));
        }
    }

    public IActionResult Confirmation(int id)
    {
        // TempData["Notification"] is automatically available in the view
        var order = _orderService.GetOrderById(id);
        return View(order);
    }
}

Rendering Notifications Globally (in _Layout.cshtml):

<!-- _Layout.cshtml — Global notification renderer -->
@if (TempData["Notification"] != null)
{
    var alertType = TempData["NotificationType"]?.ToString() ?? "info";
    <div class="alert alert-@alertType alert-dismissible fade show" role="alert">
        <strong>@(alertType == "success" ? "✅" : "⚠️")</strong>
        @TempData["Notification"]
        <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
    </div>
}

Example 2: Using [TempData] Attribute — Eliminate Magic Strings

// ProductsController.cs — Clean, type-safe approach
public class ProductsController : Controller
{
    // Decorated properties auto-sync with TempData dictionary
    [TempData]
    public string StatusMessage { get; set; }

    [TempData]
    public string StatusType { get; set; }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Delete(int id)
    {
        var product = await _repository.GetByIdAsync(id);
        if (product == null)
        {
            StatusMessage = "Product not found.";
            StatusType = "warning";
            return RedirectToAction(nameof(Index));
        }

        await _repository.DeleteAsync(id);

        StatusMessage = $"Product '{product.Name}' has been permanently deleted.";
        StatusType = "danger";

        return RedirectToAction(nameof(Index));
    }
}

Example 3: Passing Complex Objects via JSON Serialization

// TempData can only store primitive types natively.
// For complex objects, serialize to JSON first:

using System.Text.Json;

// SETTING complex data
var orderSummary = new OrderSummary { OrderId = 4521, Total = 149.99m, ItemCount = 3 };
TempData["OrderSummary"] = JsonSerializer.Serialize(orderSummary);

// READING complex data (in the next request)
if (TempData["OrderSummary"] is string json)
{
    var summary = JsonSerializer.Deserialize<OrderSummary>(json);
    // Use summary.OrderId, summary.Total, etc.
}

// PRO TIP: Create extension methods for cleaner usage
public static class TempDataExtensions
{
    public static void Set<T>(this ITempDataDictionary tempData, string key, T value)
        => tempData[key] = JsonSerializer.Serialize(value);

    public static T? Get<T>(this ITempDataDictionary tempData, string key)
    {
        if (tempData.TryGetValue(key, out var obj) && obj is string json)
            return JsonSerializer.Deserialize<T>(json);
        return default;
    }
}

5. Keep() and Peek() — Controlling TempData Lifetime

By default, reading TempData marks it for deletion. Sometimes you need to read without consuming, or extend its life for one more request:

Peek(key) — Read Without Consuming

Reads the value but does NOT mark it for deletion. The data remains available for the subsequent request.

// Value survives to the next request
var msg = TempData.Peek("StatusMessage");
// msg has the value, but TempData["StatusMessage"] 
// will STILL be available in the next request
Keep(key) — Retain After Reading

If you've already read the value (which marks it for deletion), calling Keep() cancels the deletion for the next request.

// Step 1: Read (auto-marks for deletion)
var msg = TempData["StatusMessage"];

// Step 2: Cancel deletion — keep for one more request
TempData.Keep("StatusMessage");

// Or keep ALL TempData items
TempData.Keep();

6. ViewData vs ViewBag vs TempData — The Complete Comparison

FeatureViewDataViewBagTempData
TypeDictionary<string, object>dynamic wrapperDictionary<string, object>
ScopeCurrent request onlyCurrent request onlyCurrent + next request
Survives Redirect?❌ No❌ No✅ Yes
Type-Safe?No (requires casting)No (dynamic)No (requires casting)
StorageIn-memory (request)In-memory (request)Cookie or Session
Best ForPage titles, metadataQuick ad-hoc dataFlash messages after redirect
Read BehaviorUnlimited readsUnlimited readsAuto-deleted after first read

7. Best Practices & Production Guidelines

✅ DO
  • Use TempData only for flash notifications after PRG redirects
  • Use the [TempData] attribute to avoid magic string keys
  • Create extension methods for complex object serialization
  • Place notification rendering in _Layout.cshtml for global visibility
  • Keep payloads small (< 4KB for cookie provider)
❌ DON'T
  • Don't use TempData as a substitute for Session or database storage
  • Don't store large objects or lists — serialize to JSON if needed
  • Don't store sensitive data (passwords, tokens) — it's in a cookie!
  • Don't rely on TempData for multi-step wizards — use Session or DB instead
  • Don't forget that TempData auto-deletes on read — use Peek/Keep if needed
Production Gotcha: Cookie Size Overflow

If you store too much data in TempData with the default cookie provider, the response header exceeds the browser's cookie limit (~4KB). The request will silently fail with no error message — the data just vanishes. In production, always validate payload size or switch to the Session provider for larger payloads.

8. Interview Mastery

Q: "When would you use TempData instead of Session in a production application?"

Architect Answer: "I use TempData exclusively for ephemeral notification messages following the Post-Redirect-Get pattern — success alerts, form validation summaries, or one-time warnings. TempData self-destructs after being read, so I never have to worry about cleanup or stale data. For anything that needs to persist across multiple requests — like a multi-step wizard, a shopping cart, or user preferences — I use Session backed by a distributed cache like Redis. The key architectural distinction is: TempData is for one-time messages, Session is for multi-request state, and a database is for persistent state."

ASP.NET Core MVC Mastery
1. Core Framework
Introduction to ASP.NET Core MVC
MODULE 1: INTRODUCTION & ENVIRONMENT SETUP
Microsoft Web Stack Overview Evolution of ASP.NET Environment Setup
2. View Engine
Layouts & Partial Views in Razor
MODULE 2: .NET CORE FUNDAMENTALS
Core Concepts Project Structure Startup Flow Middleware Pipeline
MODULE 3: ASP.NET CORE BASICS
Creating Project CLI Commands wwwroot & Static Files
MODULE 4: MVC FUNDAMENTALS
MVC Architecture Dependency Injection (DI) Service Lifetimes
MODULE 5: DATA PASSING TECHNIQUES
ViewData vs ViewBag TempData ViewModel Pattern
MODULE 6: ROUTING
Conventional vs Attribute Routing Custom Constraints
MODULE 7: VIEWS & UI
Razor View Engine Layouts & Sections View Components
MODULE 8: ACTION RESULTS
ViewResult JsonResult RedirectResult
MODULE 9: HTML HELPERS
Form Helpers Custom HTML Helpers
MODULE 10: TAG HELPERS
Built-in Tag Helpers Custom Tag Helpers
MODULE 11: MODEL BINDING
FromQuery vs FromRoute Complex Binding
MODULE 12: VALIDATION
Data Annotations Remote Validation Fluent Validation
MODULE 13: STATE MANAGEMENT
Cookies & Sessions TempData
MODULE 14: FILTERS & SECURITY
Action Filters Authorize Filters Anti-forgery
MODULE 15: ENTITY FRAMEWORK CORE (DEEP DIVE)
DbContext Migrations LINQ Relationships
MODULE 16: DESIGN PATTERNS
Repository Pattern Unit of Work Clean Architecture
MODULE 17: FILE HANDLING
File Upload/Download PDF/Excel Generation
MODULE 18: ADVANCED ASP.NET CORE
Request Lifecycle Bundling & Minification Deployment
MODULE 19: PERFORMANCE & BEST PRACTICES
Caching Strategies Async Programming Secure Coding
MODULE 20: RAZOR PAGES (BONUS)
Razor Pages vs MVC
MODULE 21: REAL-WORLD PROJECTS (🔥 MUST DO)
E-Commerce Web Application Employee Management System