ViewBag, ViewData and TempData - asp.net-mvc-3

Could any body explain, when to use
TempData
ViewBag
ViewData
I have a requirement, where I need to set a value in a controller one, that controller will redirect to Controller Two and Controller Two will render the View.
I have tried to use ViewBag, the value gets lost by the time I reach Controller Two.
Can I know when to use and advantages or disadvantages?
Thanks

1)TempData
Allows you to store data that will survive for a redirect. Internally it uses the Session as backing store, after the redirect is made the data is automatically evicted. The pattern is the following:
public ActionResult Foo()
{
// store something into the tempdata that will be available during a single redirect
TempData["foo"] = "bar";
// you should always redirect if you store something into TempData to
// a controller action that will consume this data
return RedirectToAction("bar");
}
public ActionResult Bar()
{
var foo = TempData["foo"];
...
}
2)ViewBag, ViewData
Allows you to store data in a controller action that will be used in the corresponding view. This assumes that the action returns a view and doesn't redirect. Lives only during the current request.
The pattern is the following:
public ActionResult Foo()
{
ViewBag.Foo = "bar";
return View();
}
and in the view:
#ViewBag.Foo
or with ViewData:
public ActionResult Foo()
{
ViewData["Foo"] = "bar";
return View();
}
and in the view:
#ViewData["Foo"]
ViewBag is just a dynamic wrapper around ViewData and exists only in ASP.NET MVC 3.
This being said, none of those two constructs should ever be used. You should use view models and strongly typed views. So the correct pattern is the following:
View model:
public class MyViewModel
{
public string Foo { get; set; }
}
Action:
public Action Foo()
{
var model = new MyViewModel { Foo = "bar" };
return View(model);
}
Strongly typed view:
#model MyViewModel
#Model.Foo
After this brief introduction let's answer your question:
My requirement is I want to set a value in a controller one, that
controller will redirect to ControllerTwo and Controller2 will render
the View.
public class OneController: Controller
{
public ActionResult Index()
{
TempData["foo"] = "bar";
return RedirectToAction("index", "two");
}
}
public class TwoController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Foo = TempData["foo"] as string
};
return View(model);
}
}
and the corresponding view (~/Views/Two/Index.cshtml):
#model MyViewModel
#Html.DisplayFor(x => x.Foo)
There are drawbacks of using TempData as well: if the user hits F5 on the target page the data will be lost.
Personally I don't use TempData neither. It's because internally it uses Session and I disable session in my applications. I prefer a more RESTful way to achieve this. Which is: in the first controller action that performs the redirect store the object in your data store and user the generated unique id when redirecting. Then on the target action use this id to fetch back the initially stored object:
public class OneController: Controller
{
public ActionResult Index()
{
var id = Repository.SaveData("foo");
return RedirectToAction("index", "two", new { id = id });
}
}
public class TwoController: Controller
{
public ActionResult Index(string id)
{
var model = new MyViewModel
{
Foo = Repository.GetData(id)
};
return View(model);
}
}
The view stays the same.

TempData
Basically it's like a DataReader, once read, data will be lost.
Check this Video
Example
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
String str = TempData["T"]; //Output - T
return View();
}
}
If you pay attention to the above code, RedirectToAction has no impact over the TempData until TempData is read. So, once TempData is read, values will be lost.
How can i keep the TempData after reading?
Check the output in Action Method Test 1 and Test 2
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
string Str = Convert.ToString(TempData["T"]);
TempData.Keep(); // Keep TempData
return RedirectToAction("Test2");
}
public ActionResult Test2()
{
string Str = Convert.ToString(TempData["T"]); //OutPut - T
return View();
}
}
If you pay attention to the above code, data is not lost after RedirectToAction as well as after Reading the Data and the reason is, We are using TempData.Keep(). is that
In this way you can make it persist as long as you wish in other controllers also.
ViewBag/ViewData
The Data will persist to the corresponding View

ViewBag, ViewData, TempData and View State in MVC
http://royalarun.blogspot.in/2013/08/viewbag-viewdata-tempdata-and-view.html
ASP.NET MVC offers us three options ViewData, VieBag and TempData for passing data from controller to view and in next request. ViewData and ViewBag are almost similar and TempData performs additional responsibility.
Similarities between ViewBag & ViewData :
Helps to maintain data when you move from controller to view. Used to
pass data from controller to corresponding view. Short life means
value becomes null when redirection occurs. This is because their goal
is to provide a way to communicate between controllers and views. It’s
a communication mechanism within the server call.
Difference between ViewBag & ViewData:
ViewData is a dictionary of objects that is derived from
ViewDataDictionary class and accessible using strings as keys. ViewBag
is a dynamic property that takes advantage of the new dynamic features
in C# 4.0. ViewData requires typecasting for complex data type and
check for null values to avoid error. ViewBag doesn’t require
typecasting for complex data type.
ViewBag & ViewData Example:
public ActionResult Index()
{
ViewBag.Name = "Arun Prakash";
return View();
}
public ActionResult Index()
{
ViewData["Name"] = "Arun Prakash";
return View();
}
In View, we call like below:
#ViewBag.Name
#ViewData["Name"]
TempData:
Helps to maintain data when you move from one controller to other
controller or from one action to other action. In other words when you
redirect, “Tempdata” helps to maintain data between those redirects.
It internally uses session variables. TempData is meant to be a very
short-lived instance, and you should only use it during the current
and the subsequent requests only
The only scenario where using TempData will reliably work is when you are redirecting. This is because a redirect kills the current request (and sends HTTP status code 302 Object Moved to the client), then creates a new request on the server to serve the redirected view.
It requires typecasting for complex data type and check for null values to avoid error.
public ActionResult Index()
{
var model = new Review()
{
Body = "Start",
Rating=5
};
TempData["ModelName"] = model;
return RedirectToAction("About");
}
public ActionResult About()
{
var model= TempData["ModelName"];
return View(model);
}

void Keep()
Calling this method with in the current action ensures that all the items in TempData are not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep(); // retains all strings values
}
void Keep(string key)
Calling this method with in the current action ensures that specific item in TempData is not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep("emp"); // retains only "emp" string values
}

TempData
will be always available until first read, once you read it its not available any more can be useful to pass quick message also to view that will be gone after first read.
ViewBag
Its more useful when passing quickly piece of data to the view, normally you should pass all data to the view through model , but there is cases when you model coming direct from class that is map into database like entity framework
in that case you don't what to change you model to pass a new piece of data, you can stick that into the viewbag
ViewData is just indexed version of ViewBag and was used before MVC3

Also the scope is different between viewbag and temptdata. viewbag is based on first view (not shared between action methods) but temptdata can be shared between an action method and just one another.

Related

Using MVC Core Action Method, I want to pass JsonResult to the view. Data is retrieved from Models. What is the best way to do the same?

Method 1:
Here data retrieved from database is converted to Json in Repository class and is passed to Controller
Repository class method to retrieve data:
public JsonResult GetEmployee(int Id)
{
Employee emp = this._employeeList.FirstOrDefault(e => e.Id == Id);
return new JsonResult(emp);
}
Action Method in Controller:
public ViewResult Details()
{
JsonResult model = _employeeRepository.GetEmployee(1);
return View(model);
}
Method 2:
Here data retrieved from database is converted to Json in Controller class at respective action Method
Repository class method to retrieve data:
public Employee GetEmployee(int Id)
{
return this._employeeList.FirstOrDefault(e => e.Id == Id);
}
Action Method in Controller:
public ViewResult Details()
{
Employee model = _employeeRepository.GetEmployee(1);
return View(Json(model));
}
Which is the best method in terms of performance and other parameters?
P.s: I want to use the json data passed in View for Ajax scripts to display computed data
I would only render JSON into a view if I needed it to be part of a html response, in which case I'd serialise it as a string first, and pass it through as a property of my model.
If I also want to pass the same data via ajax, I would write a second function in my controller e.g. Details_ajax, which would return JsonResult and use return Json(data) to serialise the data.
In general, avoiding serialisation and deserialisation is most efficient where possible.

MVC Controller/View removing model attribute

I'm creating a small MVC application and am passing a User object from a controller to an ActionResult method in another controller. One of the attributes of the User object is a list of Property objects called Properties.
Here's the rub: when the User object is finally passed to the relevant View, it's list does not contain any properties.
Here's the setup:
User class:
public class User
{
public int Id {get;set;}
public List<Property> Properties {get;set;}
}
AccountController
public ActionResult LogOn(int userId, string cryptedHash)
{
//code to logOn (this works, promise)
User user = dbContext.getUser(userId);
//debugging shows the user contains the list of properties at this point
return RedirectToAction("UserHome", "Home", user);
}
HomeController
public ActionResult UserHome(User user)
{
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user); //debugging shows that user.Properties is now empty(!)
}
UserHome.cshtml View
#model emAPI.Model_Objects.User
#{
ViewBag.Title = "UserHome";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>UserHome</h2>
<div>
#Model.Forename, these are your properties:
<ul>
#foreach (var property in #Model.Properties)
{
<li>property.Name</li>
}
</ul>
</div>
The view loads without any problem - #Model.Forename is fine, but as far as HomeController is concerned user.Properties was empty when it received it, although I know it wasn't when AccountController sent it.
Any help or advice anyone has to offer would be gratefully received.
You cannot pass entire complex objects when redirecting. Only simple scalar arguments.
The standard way to achieve that is to authenticate the user by emitting a forms authentication cookie which will allow you to store the user id across all subsequent actions. Then if in a controller action you need user details such as forename or whatever you simply query your data store to retrieve the user from wherever it is stored using the id. Just take a look at the way the Account controller is implemented when you create a new ASP.NET MVC 3 application.
So:
public ActionResult LogOn(int userId, string cryptedHash)
{
//code to logOn (this works, promise)
User user = dbContext.getUser(userId);
//debugging shows the user contains the list of properties at this point
// if you have verified the credentials simply emit the forms
// authentication cookie and redirect:
FormsAuthentication.SetAuthCookie(userId.ToString(), false);
return RedirectToAction("UserHome", "Home");
}
and in the target action simply fetch the user id from the User.Identity.Name property:
[Authorize]
public ActionResult UserHome(User user)
{
string userId = User.Identity.Name;
User user = dbContext.getUser(int.Parse(userId));
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user);
}
Ah and please, don't use ViewBag. Use view models instead. If all that your view cares about is welcoming the user by displaying his forename simply build a view model containing the forename property and then pass this view model to the view. The view doesn't care about your User domain model and it shouldn't.
RedirectToAction method returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. You should not think about passing a complex object in that to the next action method.
In this case, may be you can keep your user object in the Session variable and access it in the remaining places.
public ActionResult LogOn(int userId, string cryptedHash)
{
User user = dbContext.getUser(userId);
if(user!=null)
{
Session["LoggedInUser"]=user;
return RedirectToAction("UserHome", "Home");
}
}
public ActionResult UserHome()
{
var loggedInUser= Session["LoggedInUser"] as User;
if(loggedInUser!=null)
{
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user);
}
return("NotLoggedIn");
}

Return a view from Post method

I have this post method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Invitations(SuperInvitationsEditModel model)
{
...
var newmodel = new SuperInvitationsEditModel();
if (hasErrors)
{
SuperInvitationsErrorModel newErrorModel = new SuperInvitationsErrorModel();
newErrorModel.Errors = model.Errors;
return View(newErrorModel);
}
return View(newmodel);
}
When this code in the if(hasErrors) executes I get this error.
The model item passed into the dictionary is of type 'MyProject.Models.SuperInvitationsErrorModel', but this dictionary requires a model item of type 'MyProject.Models.SuperInvitationsEditModel'.
I thought I can do this since the return value of the method is a generic ActionResult. Can anyone tell me why is this not working?
because your current view is strongly typed. change the code as
return View("yourviewname",newErrorModel);
It has nothing to do with casting ViewResult to ActionResult. The problem is, that you have strongly typed view that expects the model of type SuperInvitationsEditModel (see #model on the top of Invitations.cshtml), but you are passing the model of type SuperInvitationsErrorModel to it.
You should merge the two view model classes (SuperInvitationsEditModel and SuperInvitationsErrorModel) into one, or create a standalone view for each of them.

What order are objects placed into a Model in MVC3 and how can I control it

I have an model being passed back successfully from my view except that I would like an ID value to be the first thing that is bound back to the Model so it can fill some information from the db. The information being passed back is as follows
SetupID:91c16e34-cf7d-e111-9b66-d067e53b2ed6
SwayBarLinkLengthLF:
SwayBarLinkLengthRF:
.....way more information....
My Action is as follows
[HttpPostAttribute]
public ActionResult SaveSetup(SetupAggregate setup)
{
setup.SaveSetup();
return null;
}
I would like the SetupID to be the first property that is set on the empty setup object but it looks like the first property alphabetically is being set first.
In my opinion you really need to be working with 2 separate "models" - your ViewModel, which is what is in your MVC project and is rendered to your view. And a 2nd EntityModel in a business logic layer. This is standard "enterprise" programming design. It gives you a lot more control of your data. The idea is this.
UI Assembly (MVC project)
ViewModel definition
public class MyModel {
public int ID { get; set; }
.... // bunch of other properties
}
Controller
public class InterestingController : Controller {
public ActionResult CreateNewWidget() {
var model = new MyModel();
return View(model);
}
[HttpPost]
public ActionResult CreateNewWidget(MyModel model) {
if(ModelState.IsValid) {
// your ctor can define the order of your properties being sent in and you can set the entity values in the ctor body however you choose to. Note never SET an ID/Primary key on a Create, let the DB handle that. If you need to return the new Key value, get it from the insert proc method in your DAL and return it up the stack
var entityModel = new EntityFromBLL(model.Name, model.OtherProperty, ... etc);
entityModel.Save(User.Identity.Name); // your save method should always capture WHO is doing the action
}
return View(model);
}
public ActionResult UpdateExistingWidget(int id) {
var entityModel = new EntityFromBLL(id); // get the existing entity from the DB
var model = new MyModel(entityModel.ID, entityModel.Name, ... etc); // populate your ViewModel with your EntityModel data in the ViewModel ctor - note remember to also create a parameterless default ctor in your ViewModel as well anytime you create a ctor in a ViewModel that accepts parameters
return View(model);
}
[HttpPost]
public ActionResult UpdateExistingWidget(MyModel model) {
if(ModelState.IsValid) {
var entityModel = new EntityFromBLL(model.ID); // always pull back your original data from the DB, in case you deal with concurrency issues
// now go thru and update the EntityModel with your new ViewModel data
entityModel.Name = model.Name;
//... etc set all the rest of the properties
// then call the save
entityModel.Save(User.Identity.Name);
}
return View(model)
}
}
Your Entity Model should be defined with private fields, public properties, a ctor that takes in all required fields for an insert (minus the primary key), a ctor that takes in the primary key and then can call an internal load method statically to return a populated object. Business rules and property validation and a single Save method. The Save method should check for the IsDirty bit after all the properties have been set and call the respective Insert or Update methods .. which in turn should call into a DAL passing DTOs

ASP.NET MVC 3 _Layout.cshtml Controller

Can anyone help me with the subject? I'm using Razor view engine and I need to pass some data to _Layout. How can I do it?
As usual you start by creating a view model representing the data:
public class MyViewModel
{
public string SomeData { get; set; }
}
then a controller which will fetch the data from somewhere:
public class MyDataController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
SomeData = "some data"
};
return PartialView(model);
}
}
then a corresponding view (~/Views/MyData/Index.cshtml) to represent the data:
#{
Layout = null;
}
<h2>#Model.SomeData</h2>
and finally inside your _Layout.cshtml include this data somewhere:
#Html.Action("index", "mydata")
You could use the ViewBag to pass data.
In your controller:
ViewBag.LayoutModel = myData;
Access in you layout:
#ViewBag.LayoutModel
It is a dynamic object, so you can use any property name you want.
The ViewBag method is the easiest. However if you need advanced and typed features, you can also try taking that part to a partial view (the part that'll render the dependent section) with a common controller (if the value can be calculated on it's own and doesn't need input from other controllers), and call RenderPartial on it from _Layout.
If you'd like I can give you some more info about it...

Resources