As ASP.NET Core developers, we often need to transfer data between controllers, views, requests, and even user sessions. The framework provides several mechanisms for this purpose: ViewData, ViewBag, TempData, and Session.
Many junior developers learn these concepts separately and end up using them interchangeably. In reality, each one solves a completely different problem, has a different lifecycle, and affects maintainability in different ways.
Understanding when to use each mechanism is essential for building scalable and maintainable MVC applications.
Why So Many Options Exist?
Imagine an e-commerce application.
Sometimes you need to pass data from a controller to a view.
Sometimes you need data to survive a redirect.
Sometimes you need information available across multiple requests.
Sometimes you need user-specific state that remains available during an entire browsing session.
These are fundamentally different requirements. Microsoft introduced different mechanisms because a single solution would either be inefficient or overly complex.
The key distinction is scope and lifetime.
ViewData
ViewData is the oldest mechanism inherited from classic ASP.NET MVC.
Internally, it is a dictionary of type:
ViewDataDictionary
which stores data as key-value pairs.
A controller can populate the dictionary and the view can read from it.
public IActionResult Details()
{
ViewData["Title"] = "Product Details";
ViewData["ProductCount"] = 15;
return View();
}
Inside the Razor view:
<h1>@ViewData["Title"]</h1>
<p>Total Products: @ViewData["ProductCount"]</p>
Although simple, ViewData has a major weakness.
Because everything is stored as object, type casting becomes necessary.
int count = (int)ViewData["ProductCount"];
This means compile-time safety is lost.
If someone changes the type later, the application may fail at runtime.
For this reason, ViewData is generally considered suitable only for small pieces of UI-related information.
Examples include:
Page titles
Breadcrumb text
Display messages
Minor metadata
Using ViewData for complex business objects usually creates maintenance problems.
ViewBag
ViewBag was introduced to make ViewData easier to use.
Interestingly, ViewBag is not a separate storage mechanism.
Internally it wraps ViewData using dynamic properties.
When you write:
ViewBag.Title = "Product Details";
ASP.NET Core actually stores the value inside ViewData.
The equivalent ViewData version is:
ViewData["Title"] = "Product Details";
The controller:
public IActionResult Details()
{
ViewBag.ProductName = "Gaming Laptop";
ViewBag.Price = 1500;
return View();
}
The view:
<h2>@ViewBag.ProductName</h2>
<p>Price: @ViewBag.Price €</p>
The syntax is cleaner and easier to read.
However, ViewBag introduces another problem: the use of dynamic.
Consider:
ViewBag.ProductName
the compiler will not detect the typo.
The error appears only during execution.
For small MVC applications this may not matter, but in enterprise systems it becomes a source of bugs.
That is why many senior developers prefer strongly typed ViewModels over both ViewData and ViewBag.
ViewData vs ViewBag
Since both use the same underlying storage, their lifecycle is identical.
They exist only during the current request.
Once the response is sent, the data disappears.
The following diagram describes their lifecycle conceptually:
Controller → View → Destroyed
Neither survives redirects.
Consider:
public IActionResult Save()
{
ViewBag.Message = "Saved";
return RedirectToAction("Index");
}
The message will be lost.
The redirect creates an entirely new HTTP request.
This is where TempData enters the picture.
TempData
TempData was designed specifically for passing data between requests.
Most commonly, it is used after redirects.
A typical scenario is the Post-Redirect-Get pattern.
[HttpPost]
public IActionResult Save(Product product)
{
// Save logic
TempData["Success"] = "Product saved successfully";
return RedirectToAction("Index");
}
Then:
public IActionResult Index()
{
return View();
}
Inside the view:
@if (TempData["Success"] != null)
{
<div class="alert alert-success">
@TempData["Success"]
</div>
}
The user sees the message after the redirect.
This would not be possible using ViewData or ViewBag.
The Special Behavior of TempData
TempData behaves differently from the other mechanisms.
By default, data is automatically removed after it is read.
For example:
var message = TempData["Success"];
After this line executes, the value is marked for deletion.
If you need to preserve it:
TempData.Keep();
Or:
TempData.Keep("Success");
You can also inspect a value without removing it:
var message = TempData.Peek("Success");
This makes TempData ideal for:
Success notifications
Error messages
Wizard workflows
Redirect scenarios
It is not suitable for long-term storage.
Session
Session solves a completely different problem.
While ViewData, ViewBag, and TempData focus on controller/view communication, Session stores user-specific state across many requests.
Imagine a shopping cart.
A user may browse dozens of pages before checking out.
The cart must remain available throughout the session.
HttpContext.Session.SetString(
"Username",
"Nick"
);
Reading:
var username =
HttpContext.Session.GetString(
"Username"
);
Unlike ViewData and TempData, Session can remain available for minutes or hours depending on configuration.
Configuring Session in ASP.NET Core
First, register Session services:
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout =
TimeSpan.FromMinutes(30);
});
Then enable middleware:
app.UseSession();
Now Session becomes available through:
HttpContext.Session
Without this configuration, Session operations will fail.
Session and Complex Objects
Session natively supports strings and byte arrays.
For custom objects, serialization is required.
Example:
public class ShoppingCart
{
public int ItemCount { get; set; }
}
Store:
var cart = new ShoppingCart
{
ItemCount = 5
};
HttpContext.Session.SetString(
"Cart",
JsonSerializer.Serialize(cart)
);
Retrieve:
var json =
HttpContext.Session.GetString(
"Cart"
);
var cart =
JsonSerializer.Deserialize<ShoppingCart>(
json
);
This approach is common in real-world applications.
Performance Considerations
A frequent mistake is treating Session as a database.
Session should store only small amounts of user-specific state.
Storing large datasets increases memory consumption and hurts scalability.
In distributed environments, Session data often ends up in:
- Redis
- SQL Server
- Distributed Cache Providers
The larger the Session, the slower the application becomes.
A good rule is simple:
If data belongs in a database, keep it in a database.
What Senior Developers Usually Choose
In modern ASP.NET Core applications, strongly typed ViewModels are generally preferred over ViewData and ViewBag.
Example:
public class ProductViewModel
{
public string Name { get; set; }
public decimal Price { get; set; }
}
Controller:
public IActionResult Details()
{
var model =
new ProductViewModel
{
Name = "Laptop",
Price = 1500
};
return View(model);
}
View:
@model ProductViewModel
<h2>@Model.Name</h2>
<p>@Model.Price €</p>
This provides:
- Compile-time checking
- IntelliSense support
- Easier refactoring
- Better maintainability
Consequently, ViewData and ViewBag are often reserved for small UI metadata.
Modern ASP.NET Core Perspective
While ViewData, ViewBag, TempData, and Session remain fully supported in ASP.NET Core, their role has evolved. Modern applications favor strongly typed ViewModels, DTOs, and API-based architectures because they provide better maintainability, testability, and compile-time safety. ViewData and ViewBag are now typically reserved for small UI-related values, TempData remains a practical solution for passing messages across redirects, and Session is used more selectively for user-specific state. The key takeaway is that these mechanisms are still valuable, but in contemporary ASP.NET Core development they are no longer the primary way application data is modeled and transferred.
Final Verdict
The biggest mistake developers make is viewing these four mechanisms as alternatives. They are not.
ViewData and ViewBag exist to pass data from a controller to a view during a single request.
TempData exists to transfer data between requests, especially after redirects.
Session exists to maintain user-specific state across multiple requests and pages.
When selecting one, ask a single question:
How long should the data live?
If the answer is until the view renders, use ViewData, ViewBag, or preferably a ViewModel.
If the answer is until the next request, use TempData.
If the answer is throughout the user's interaction with the application, use Session.
Understanding this distinction is one of those small architectural decisions that separates maintainable ASP.NET Core applications from those that become increasingly difficult to evolve over time.
Συμπεράσματα
Ένα από τα μεγαλύτερα λάθη που κάνουν οι προγραμματιστές είναι να αντιμετωπίζουν τα ViewData, ViewBag, TempData και Session ως εναλλακτικές λύσεις για το ίδιο πρόβλημα. Στην πραγματικότητα, δεν είναι.
Το ViewData και το ViewBag υπάρχουν για να μεταφέρουν δεδομένα από έναν Controller σε ένα View κατά τη διάρκεια του ίδιου HTTP request.
Το TempData σχεδιάστηκε για τη μεταφορά δεδομένων μεταξύ διαδοχικών requests, κυρίως σε σενάρια όπου μεσολαβεί κάποιο redirect.
Το Session εξυπηρετεί έναν εντελώς διαφορετικό σκοπό: τη διατήρηση δεδομένων που αφορούν τον συγκεκριμένο χρήστη σε πολλαπλά requests και σε διαφορετικές σελίδες της εφαρμογής.
Όταν έρχεται η στιγμή να επιλέξετε ποιον μηχανισμό θα χρησιμοποιήσετε, κάντε πρώτα μία απλή ερώτηση:
«Για πόσο χρονικό διάστημα πρέπει να παραμείνουν διαθέσιμα αυτά τα δεδομένα;»
Αν τα δεδομένα χρειάζονται μόνο μέχρι να γίνει η απόδοση του View, τότε το ViewData, το ViewBag ή ακόμη καλύτερα ένα ισχυρά τυποποιημένο ViewModel αποτελούν τις κατάλληλες επιλογές.
Αν τα δεδομένα πρέπει να επιβιώσουν μέχρι το επόμενο request, τότε το TempData είναι η σωστή λύση.
Αν τα δεδομένα πρέπει να παραμείνουν διαθέσιμα καθ' όλη τη διάρκεια της αλληλεπίδρασης του χρήστη με την εφαρμογή, τότε το Session είναι ο κατάλληλος μηχανισμός.
Η κατανόηση αυτής της διαφοράς μπορεί να φαίνεται ως μια μικρή αρχιτεκτονική λεπτομέρεια. Στην πράξη, όμως, είναι μία από εκείνες τις αποφάσεις που διαχωρίζουν τις καθαρές, επεκτάσιμες και εύκολα συντηρήσιμες εφαρμογές ASP.NET Core από εκείνες που με την πάροδο του χρόνου γίνονται ολοένα και πιο δύσκολες στη συντήρηση και την εξέλιξή τους.
Top comments (0)