What is the right time for ViewData, ViewBag, Session, TempData - asp.net-mvc

I was editing a project and I saw a Session[""] in one controller method and TempData[""] in another. Is there a difference between the 4 or is it just 4 ways to do the same thing.

ViewData/ViewBag - valid only for the duration of the current request. You set it in a controller action and use it in the view, then it disappears. The difference is that the first is a dictionary whereas the second is just a dynamic wrapper around this dictionary. Both point to the same data though. ViewBag was introduced in ASP.NET MVC 3.
Example:
public ActionResult Index()
{
ViewData["foo"] = "bar";
return View();
}
and inside the view you could use this value:
<div>#ViewData["foo"]</div>
Same with ViewBag but it is dynamic:
public ActionResult Index()
{
ViewBag.foo = "bar";
return View();
}
and inside the view you could use this value:
<div>#ViewBag.foo</div>
So as you can see ViewData/ViewBag are just an alternative way to pass information to a view from a controller action compared to the classic and recommended way which is using a view model:
public class MyViewModel
{
public string Foo { get; set; }
}
and then:
public ActionResult Index()
{
var model = new MyViewModel { Foo = "bar" };
return View(model);
}
and inside your strongly typed view:
#model MyViewModel
<div>#Html.DisplayFor(x => x.Foo)</div>
As you can see using view models provide a strongly typed approach in passing information to a view from a controller action.
TempData - it allows for persisting information for the duration of a single subsequent request. You store something inside TempData and then redirect. In the target controller action to which you redirected you could retrieve the value that was stored inside TempData.
Example:
public ActionResult Foo()
{
TempData["foo"] = "bar";
return RedirectToAction("bar");
}
public ActionResult Bar()
{
var value = TempData["foo"] as string;
// use the value here. If you need to pass it to the view you could
// use ViewData/ViewBag (I can't believe I said that but I will leave it for the moment)
return View();
}
ASP.NET MVC will automatically expire the value that was stored in TempData once you read it. Under the covers ASP.NET MVC persists the information into the Session.
Session - same as TempData except that it never expires - it will be valid for all requests, not a single redirect.

ASP.net MVC introduced ViewData, ViewBag, TempData, Session to pass data between controller to view.
ViewData
ViewData is implemented by using ViewDataDictionary class which stored in CurrentRequestContext. So, ViewData life-cycle will end when the current request ends.
ViewBag is also like ViewData, and only difference is it enable dynamically sharing the data using dynamics objects.
TempData is a very short-lived instance, and you should only use it during the current and the subsequent requests only.This will be handy if you want to use Redirections(RedirectToAction, RedirectToRoute, Redirect) in ASP.net MVC and pass some data among redirects. TempData stores data in Session but framework disposes the data when current and subsequent requests ends.
Session is long-lived(Never expires) data that belongs to user session.You need to be mindful when you use session variables which can be easily cause issues.
protected void Session_Start(Object sender, EventArgs e)
{
int userType = 1;
HttpContext.Current.Session.Add("_SessionUserType",userType );
}

ViewData:
Is a special dictionary inherited from ViewDataDictionary.
Used to send data from controller to view.
It's life span is the current request.
It will be destroyed if you have Redirect.
For security reasons, it's better to check it for null before usage.
The casting should be done for the operation.
ViewBag:
Is a dynamic type (this type is presented in c#4).
Like ViewData is used to send data from the controller to the view.
The duration of the validity of its values ​​in the current request.
In redirection between pages, its value will be null.
For security reasons before use, check it for null.
The casting is not necessary, so it's more faster than ViewData.
TempData:
A special kind of dictionary derived from TempDataDictionary.
It has Short life time, and used to send information between pages (Redirect).
After rendering the View completely, its value will be null.
For security reasons before use, check it for null.
The casting should be done for the operation.
Session:
used To send information between different requests.
Its value is not null not null values; Unless after a certain time (session expire).
For security reasons before use, check it for null.
The casting should be done for the operation.
This article explains the difference between ViewData, ViewBag and TempData. I hope you can refer this article for your need.

Related

MVC index method with change to viewmodel

I want to know the best way of writing an index method in MVC when we need to make changes to the model before passing it to the view.
I picked up a technique from somewhere where you can pass in the model you intend to pass to your view, and make changes to it, but we have noticed that model binding kicks in when passing to the index method, and validation fires, when there is no need for it to because it is the initial load.
For example, which is more correct:
public ActionResult Index(ViewModel model)
{
model.SomeProperty = "MyNewValue";
return base.Index(model);
}
OR
public ActionResult Index()
{
ViewModel model = new ViewModel();
model.SomeProperty = "MyNewValue";
return base.Index(model);
}
and is there anything I should know about the implications of using either one?
With initial loads, the latter makes more sense. Controllers in MVC are newed up each and every single time you use them. There is no persistence whatsoever. With the first option, HTTP requests need some pre-existing knowledge of your API. So, the recommendation is to have the view model come from a persistence layer.
public ActionResult Index()
{
var vm = /* persistence layer */
vm.SomeProperty = "NewVal";
return View(vm);
}

Pass a big object to an action on a controller from a view

I want to pass a big object to a controller's action from a view. Like so:
View
<div>#Html.ActionLink("Send us an email", "Index",
"Email", new { o = #Model.Exception }, null)</div>
Controller
public class EmailController : Controller
{
[AllowAnonymous]
public ActionResult Index(object o)
{
new BaseServices.Emailer().SendEmail(o);
return View();
}
}
The thing is: the object being passed is so large that I guess that MVC is unable to make an argument out of that and add it to the route table/dictionary. So, my email controller's Index action is never called. The code bombs off somewhere in between.
No, you can't do this. ASP.NET MVC is not some magic. It relies on standard HTTP and HTML. And as you know in HTTP when you are using a GET request, there's no notion of .NET objects. You cannot ask how to pass an object in a web application because this is not defined.
There's a notion of query string parameters. So that's what you can pass => simple query string parameters:
#Html.ActionLink(
"Send us an email",
"Index",
"Email",
new { id = Model.Exception.Id, text = Model.Exception.Text },
null
)
Where the magic comes is that ASP.NET MVC will now use the 2 simple query string parameters (id and text) to map them to the corresponding properties of your view model inside the controller action.
But of course for this to work ASP.NET MVC needs to know the type of the model. You cannot just use object because this type doesn't have id nor text properties.
So:
public ActionResult Index(MyViewModel o)
Now but what about sending complex types? Well, the question that you have to ask to yourself is why on the first place this type was passed to the view? Was it because tfhe user was supposed to edit some of its properties? Is so then you should use an HTML form containing input fields allowing the user to edit them.
But since you have stuck this object into an anchor then what's the point? The server could fetch this object from wherever it fetched it in the first place. So all you need is to pass a simple id to the server:
#Html.ActionLink(
"Send us an email",
"Index",
"Email",
new { id = Model.Exception.Id },
null
)
and have the controller action take this id as parameter:
public ActionResult Index(int id)
Alright now you know the id => therefore you could retrieve the corresponding entity from wherever this entity is persisted.
Now some people might suggest you storing the object into the session before rendering the view and then retrieving this object from the session. Personally I am not a big fan of the session as it introduces state into the application. This means that you can never invoke the second action without first invoking the first action. This also means that you cannot bookmark the second action into the browser favorites. This also means that if you are running your application in a web-farm you can no longer store the session in-memory => you will have to use an out-of-process storage for this session. Sessions are way too much of a hassle.
You can't really pass it in the view.
Instead, consider storing the exception in TempData in the controller that renders the view....
public ActionResult DisplayErrorAndOptionToEmailIt()
{
TempData["LastError"] = m.Exception;
return View();
}
and then when the request comes in retrieve it from temp data and email it
public ActionResult SendTheEmail()
{
var e = TempData["LastError"] as Exception;
if (e != null)
{
EmailHelper.SendExceptionEmail(e);
}
return View();
}
On a side note, it's not the best practice to store complete objects. If possible, store only what you need:
public ActionResult DisplayErrorAndOptionToEmailIt()
{
TempData["LastError"] = m.Exception.Message;
return View();
}

What is FormCollection argument for? Is it a dummy argument?

Up to now I have no idea why does VS provide FormCollection argument by default?
public ActionResult Edit(int id)
{
Dinner dinner = dinnerRepository.GetDinnerById(id);
if (dinner == null)
return View("NotFound");
else
return View(dinner);
}
[HttpPost]
public ActionResult Edit(int id, object dummy/*, FormCollection collection*/)
{
Dinner temp = dinnerRepository.GetDinnerById(id);
if (TryUpdateModel(temp))
{
dinnerRepository.Save();
return RedirectToAction("Details", new { id = temp.DinnerId });
}
else
return View(temp);
}
EDIT 1: In my experiment, any arguments other than id are dummy because they never be used in httppost Edit action method.
EDIT 2: Does TryUpdateModel use FormCollection behind the scene?
If your app receives a POST, then 99.99% of the time it will come from an HTML form. The FormsCollection gathers all the values in the form for you.
In ASP.NET MVC you are almost always better off using strongly typed objects though. The DefaultModelBinder will create them for you most of the time, and you can implement IModelBinder if needed if the default one doesn't do what you need.
The FormCollection is how ASP.NET provides you access to the values that were just posted to your page.
You could also use a strongly typed argument and then ASP.NET MVC would use the FormCollection internally to create your strongly typed object.
FormCollection contains your form state coming back to your server.
If you need any custom operation on the processing of your data you use FormCollection. Otherwise you can happily remove it.
I am using it heavily for a hierarchical model processing.

How does the ASP.NET MVC UpdateModel() method work?

I'm working on my first .NET MVC application and using the NerdDinner tutorial as a reference point. One point that is intriguing me at the moment is the UpdateModel() method. (I don't like using things I don't really understand.)
Taken from the NerdDinner tutorial -
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
Dinner dinner = dinnerRepository.GetDinner(id);
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
My main question is how does the UpdateModel() get access to the formValues passed in the Edit method? Why is the collection not passed in explicitly as a parameter to the method?
UpdateModel() is a Controller helper method that attempts to bind a bunch of different input data sources (HTTP POST data coming from a View, QueryString values, Session variables/Cookies, etc.) to the explicit model object you indicate as a parameter. Essentially, it is only for model binding.
If you express the input parameters for your Action as a strongly-typed model (like a View Model), you've already taken all of the steps that are done behind the scenes when UpdateModel() is called. If you retrieve an object from the DataContext and edit its properties, SaveChanges() is all you need to push the updates back to the database (in this case, Save()).
Example:
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(DinnerViewModel incoming) {
var dinner = dinnerRepository.GetDinner(incoming.DinnerID);
dinner.Description = incoming.Description;
dinnerRepository.Save();
return RedirectToAction("Details", new { id = incoming.DinnerID });
}
However, there is a use-case for using UpdateModel() with a strongly-typed model: when you are passing in a strongly-typed model and want its values to directly replace those of an entity from the database (provided they are all named and typed the same). In this case, you would retrieve the object, use UpdateModel() on it, and its model binding operation will pull in any similarly-named and typed properties from the strongly-typed object to the retrieved object. In other words, it will perform reflection for you.
So, like your example, if you want all properties to update without specifying which to update, and your strongly-typed model and database model have similarly-named properties, you would still want to use UpdateModel() to take advantage of the reflection.
Example:
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(DinnerViewModel incoming) {
var dinner = dinnerRepository.GetDinner(incoming.DinnerID);
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = incoming.DinnerID });
}
The only advantage here (over using a FormCollection object) is that you'd have access to all other properties of the strongly-typed object (as shown by incoming.DinnerID).
Conclusion: if you're translating a strongly-typed object to a derived object, it's probably easiest to use UpdateModel(). However, it's largely unnecessary if you are simply updating a few properties of the derived object. Also, be aware that use of the Entity Framework (instead of something like Linq to SQL) makes all of this moot, as it can relate strongly-typed objects and derived objects with its own methods.
It does inspect all the HttpRequest inputs such as Form, QueryString, Cookies and Server variables. I think in this order.
Instead of passing Model object as a parameter to "Post()" action method, we are creating an instance of an Model object within the "Post()" function, and updating it using "UpdateModel()" function. "UpdateModel()" function inspects all the HttpRequest inputs such as posted Form data, QueryString, Cookies and Server variables and populate the employee object.
e.g.
[HttpPost]
[ActionName("Create")]
public ActionResult Create_Post()
{
EmployeeBusinessLayer employeeBusinessLayer =
new EmployeeBusinessLayer();
Employee employee = new Employee();
UpdateModel(employee);
employeeBusinessLayer.AddEmmployee(employee);
return RedirectToAction("Index");
}

ASP.NET MVC Beta 1: DefaultModelBinder wrongly persists parameter and validation state between unrelated requests

When I use the default model binding to bind form parameters to a complex object which is a parameter to an action, the framework remembers the values passed to the first request, meaning that any subsequent request to that action gets the same data as the first. The parameter values and validation state are persisted between unrelated web requests.
Here is my controller code (service represents access to the back end of the app):
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Create()
{
return View(RunTime.Default);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(RunTime newRunTime)
{
if (ModelState.IsValid)
{
service.CreateNewRun(newRunTime);
TempData["Message"] = "New run created";
return RedirectToAction("index");
}
return View(newRunTime);
}
My .aspx view (strongly typed as ViewPage<RunTime>) contains directives like:
<%= Html.TextBox("newRunTime.Time", ViewData.Model.Time) %>
This uses the DefaultModelBinder class, which is meant to autobind my model's properties.
I hit the page, enter valid data (e.g. time = 1). The app correctly saves the new object with time = 1. I then hit it again, enter different valid data (e.g. time = 2). However the data that gets saved is the original (e.g. time = 1). This also affects validation, so if my original data was invalid, then all data I enter in the future is considered invalid. Restarting IIS or rebuilding my code flushes the persisted state.
I can fix the problem by writing my own hard-coded model binder, a basic naive example of which is shown below.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([ModelBinder(typeof (RunTimeBinder))] RunTime newRunTime)
{
if (ModelState.IsValid)
{
service.CreateNewRun(newRunTime);
TempData["Message"] = "New run created";
return RedirectToAction("index");
}
return View(newRunTime);
}
internal class RunTimeBinder : DefaultModelBinder
{
public override ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
// Without this line, failed validation state persists between requests
bindingContext.ModelState.Clear();
double time = 0;
try
{
time = Convert.ToDouble(bindingContext.HttpContext.Request[bindingContext.ModelName + ".Time"]);
}
catch (FormatException)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName + ".Time", bindingContext.HttpContext.Request[bindingContext.ModelName + ".Time"] + "is not a valid number");
}
var model = new RunTime(time);
return new ModelBinderResult(model);
}
}
Am I missing something? I don't think it's a browser session problem as I can reproduce the problem if the first data is entered in one browser and the second in another.
It turns out that the problem was that my controllers were being reused between calls. One of the details I chose to omit from my original post is that I am using the Castle.Windsor container to create my controllers. I had failed to mark my controller with the Transient lifestyle, so I was getting the same instance back on each request. Thus the context being used by the binder was being re-used and of course it contained stale data.
I discovered the problem while carefully analysing the difference between Eilon's code and mine, eliminating all other possibilities. As the Castle documentation says, this is a "terrible mistake"! Let this be a warning to others!
Thanks for your response Eilon - sorry to take up your time.
I tried to reproduce this problem but I'm not seeing that same behavior. I created almost exactly the same controller and views that you have (with some assumptions) and every time I created a new "RunTime" I put its value in TempData and sent it off through the Redirect. Then on the target page I grabbed the value and it was always the value I typed in on that request - never a stale value.
Here's my Controller:
public class HomeController : Controller {
public ActionResult Index() {
ViewData["Title"] = "Home Page";
string message = "Welcome: " + TempData["Message"];
if (TempData.ContainsKey("value")) {
int theValue = (int)TempData["value"];
message += " " + theValue.ToString();
}
ViewData["Message"] = message;
return View();
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Create() {
return View(RunTime.Default);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(RunTime newRunTime) {
if (ModelState.IsValid) {
//service.CreateNewRun(newRunTime);
TempData["Message"] = "New run created";
TempData["value"] = newRunTime.TheValue;
return RedirectToAction("index");
}
return View(newRunTime);
}
}
And here's my View (Create.aspx):
<% using (Html.BeginForm()) { %>
<%= Html.TextBox("newRunTime.TheValue", ViewData.Model.TheValue) %>
<input type="submit" value="Save" />
<% } %>
Also, I wasn't sure what the "RunTime" type looked like, so I made this one:
public class RunTime {
public static readonly RunTime Default = new RunTime(-1);
public RunTime() {
}
public RunTime(int theValue) {
TheValue = theValue;
}
public int TheValue {
get;
set;
}
}
Is it possible that your implementation of RunTime includes some static values or something?
Thanks,
Eilon
I'm not sure if this is related or not, but your call to
<%= Html.TextBox("newRunTime.Time", ViewData.Model.Time) %>
might actually pick the wrong overload (since Time is an integer, it will pick the object htmlAttributes overload, rather than string value.
Checking the rendered HTML will let you know if this is occurring. changing int to ViewData.Model.Time.ToString() will force the correct overload.
It sounds like your issue is something different, but I noticed that and have been burned in the past.
Seb, I'm not sure what you mean by an example. I don't know anything about Unity configuration. I'll explain the situation with Castle.Windsor and maybe that will help you with to configure Unity correctly.
By default, Castle.Windsor returns the same object each time you request a given type. This is the singleton lifestyle. There's a good explanation of the various lifestyle options in the Castle.Windsor documentation.
In ASP.NET MVC, each instance of a controller class is bound to the context of the web request that it was created to serve. So if your IoC container returns the same instance of your controller class each time, you'll always get a controller bound to the context of the first web request that used that controller class. In particular, the ModelState and other objects used by the DefaultModelBinder will be reused, so your bound model object and the validation messages in the ModelState will be stale.
Therefore you need your IoC to return a new instance each time MVC requests an instance of your controller class.
In Castle.Windsor, this is called the transient lifestyle. To configure it, you have two options:
XML configuration: you add lifestlye="transient" to each element in your configuration file that represents a controller.
In-code configuration: you can tell the container to use the transient lifestyle at the time you register the controller. This is what the MvcContrib helper that Ben mentioned does automatically for you - take a look at the method RegisterControllers in the MvcContrib source code.
I would imagine that Unity offers a similiar concept to the lifestyle in Castle.Windsor, so you'll need to configure Unity to use its equivalent of the transient lifestyle for your controllers.
MvcContrib appears to have some Unity support - maybe you could look there.
Hope this helps.
Having come across similar problems when attempting to use the Windsor IoC container in an ASP.NET MVC app I had to go through the same voyage of discovery to get it working. Here are some of the details that might help someone else.
Using this is the initial setup in the Global.asax:
if (_container == null)
{
_container = new WindsorContainer("config/castle.config");
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container));
}
And using a WindsorControllerFactory which when asked for a controller instance does:
return (IController)_container.Resolve(controllerType);
Whilst Windsor was correctly linking up all of the controllers, for some reason parameters were not being passed from the form to the relevant controller action. Instead they were all null, though it was calling the correct action.
The default is for the container to pass back singletons, obviously a bad thing for controllers and the cause of the problem:
http://www.castleproject.org/monorail/documentation/trunk/integration/windsor.html
However the documentation does point out that the lifestyle of the controllers can be changed to transient, though it doesn't actually tell you how to do that if you are using a configuration file. Turns out it's easy enough:
<component
id="home.controller"
type="DoYourStuff.Controllers.HomeController, DoYourStuff"
lifestyle="transient" />
And without any code changes it should now work as expected (i.e. unique controllers every time provided by the one instance of the container). You can then do all of your IoC configuration in the config file rather than the code like the good boy/girl that I know you are.

Resources