ASP.NET Core MVC Mastery

TempData

1 Views Updated 5/4/2026

TempData Mastery: Keep, Peek, and the PRG Pattern

In standard state management, Session lives until the user logs out, and Cookies live until they expire. But what if you only need data to survive for exactly one single subsequent request? That's the specialized domain of TempData. Mastering it is essential for building robust, duplicate-proof forms using the Post-Redirect-Get pattern.

1. WHAT is TempData?

TempData (backed by ITempDataDictionary) is a very short-lived state management dictionary. Data placed in TempData is persisted only until it is read. Once read, it is instantly marked for deletion at the end of the current request. By default in ASP.NET Core, TempData uses a specialized cookie to store its data, making it stateless on the server side.

The Golden Rule of TempData

Read = Delete. The absolute moment you read a value from TempData["MyKey"], the framework schedules the destruction of that key. If the user refreshes the page, the data will be completely gone.

2. The Post-Redirect-Get (PRG) Pattern

TempData exists almost exclusively to support the PRG pattern. Without PRG, if a user submits a form (POST) and clicks "Refresh" on their browser, the browser will re-submit the POST request — potentially charging their credit card twice or creating duplicate database records.

Implementing PRG with TempData

public class PaymentController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ProcessCard(PaymentDto model)
    {
        if (!ModelState.IsValid) return View(model);

        // 1. Process the actual payment (POST)
        var transactionId = await _paymentService.ChargeAsync(model);

        // 2. Load the success message into TempData
        TempData["SuccessMessage"] = $"Payment of {model.Amount:C} successful! ID: {transactionId}";

        // 3. REDIRECT. This sends a 302 to the browser, forcing a completely new GET request.
        return RedirectToAction(nameof(Receipt));
    }

    [HttpGet]
    public IActionResult Receipt()
    {
        // 4. The GET request reads TempData. It will display correctly.
        // Once read, TempData marks "SuccessMessage" for deletion.
        return View();
    }
}
<!-- Views/Payment/Receipt.cshtml -->
@if (TempData["SuccessMessage"] != null)
{
    <!-- If user refreshes this page, TempData is null, so the alert won't show again -->
    <div class="alert alert-success">@TempData["SuccessMessage"]</div>
}
<h2>Your Receipt</h2>

3. Controlling Lifespan: Keep() and Peek()

Sometimes the "Read = Delete" rule is too aggressive. You might need to read TempData but preserve it for one more request (e.g., if validation fails on the next page and you redirect again). ASP.NET Core offers two methods to manipulate the lifecycle.

TempData.Peek()

Peek(string key) allows you to read a value without marking it for deletion.

public IActionResult FirstPage()
{
    // Reads the value, but tells the framework NOT to delete it
    string message = TempData.Peek("Notification")?.ToString();
    
    // The data SURVIVES for the next request...
    return View();
}

TempData.Keep()

Keep() or Keep(string key) is used when you have already read a value using standard syntax (which marked it for deletion), but you change your mind and want to cancel the deletion.

public IActionResult FirstPage()
{
    // 1. Reads the value. It is now MARKED FOR DELETION.
    string message = TempData["Notification"]?.ToString();
    
    // 2. Logic happens... we realize we still need it.
    if (someConditionFailed)
    {
        // 3. Cancels the deletion for this specific key
        TempData.Keep("Notification"); 
        
        // Or keep EVERYTHING built up so far
        // TempData.Keep(); 
    }
    
    return RedirectToAction("SecondPage");
}

4. Complex Objects in TempData

❌ The Problem

Because TempData is serialized to a cookie by default, it only supports primitive types (string, int, bool, Guid). If you try this, your app will crash:

var user = new User { Name = "John" };
// CRASH: Cannot serialize complex object
TempData["CurrentUser"] = user;
✅ The Solution

You must serialize the object to JSON precisely before passing it to TempData, then deserialize it on the other side.

// Send
string json = JsonSerializer.Serialize(user);
TempData["CurrentUser"] = json;

// Receive
string jsonStr = TempData["CurrentUser"] as string;
var user = JsonSerializer.Deserialize<User>(jsonStr);

5. Best Practices

  • Use TempData for PRG. It is the definitive industry standard for passing "Record saved successfully" messages across redirect boundaries.
  • Don't use TempData for sensitive PII. In ASP.NET Core, it's stored in a cookie. While the payload is protected by the Data Protection API, it's still sent over the network.
  • Don't use TempData for large datasets. Cookie size limits hover around 4KB. If you encode massive JSON strings into TempData, headers will bloat and the server will return 431 Request Header Fields Too Large.

6. Interview Mastery

Q: "What is the difference between ViewData, ViewBag, and TempData?"

Architect Answer: "The difference is strictly their lifecycle. ViewData (dictionary) and ViewBag (dynamic wrapper over ViewData) only survive for the lifespan of the current HTTP request — they carry data from a Controller directly into its Razor View. Once the HTML is rendered, they are destroyed. TempData, on the other hand, survives for one additional HTTP request. It uses a cookie behind the scenes to bridge the gap between two completely separate HTTP calls, making it the required mechanism for passing notifications during a Post-Redirect-Get flow."

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