If we want to create some objects to be used by all action methods in a controller, can we store them as instance variables in the controller?
Phil Haack mentions that controllers are not meant to be reused in this old post:
Asp.Net MVC Beta: Previous RouteData overrides current RouteData?
But is this one-controller-per-request behavior guaranteed?
I don't want to be in a situation where a reused controller has data from another request.
Yes (to the question in your title) and No (to the question in your post).
A new controller instance is created for each request. You might be able to do this by using your own controller factory that caches controllers and only creates them as needed, but I wouldn't recommend it. You'd be better off to simply cache any information that is needed either in the Session or the Cache or store the information in the View (hidden, if necessary) than to go to the trouble of creating a new controller factory.
Every time you call an action method, a new instance of the controller class should be created. So it is not a good idea to have your action methods depend on instance variables. If you use the DefaultControllerFactory then you will get a new controller instance for each request. If you use a custom controller factory you could override this behavior, which I wouldn't recommend.
You can use TempData to store temporary data in your controller
Related
I've searched a lot and read a lot about the concept of MVC. But I still don't know how to connect them together. Suppose I have a controller class, view class and model class. If user did something in that view, view should notify controller the action and controller may need to communicate with model to get some data.
Now, does view hold a reference to the controller? Does the controller has a property of that model? Or they just using like "include"?
The data is saved in memory, or database? Memory means stored in variables.
More complicated case, one action from user may need many controllers and models involved. How to co-ordinate them together?
What I've done before is I create a "view controller", which has view and some actions of that view.
And sometimes there is no model. All data is passed by parameter. If there are some models, some of them are singleton so I can get it everywhere.
This is a very generic answer, depending on your system these can vary:
The Controller has a reference to the View, an IBOutlet for example. The View is not aware of the Controller, it's a dumb thing that receives info and displays it, that's it.
Depends on what you have in place, sometimes you don't need to persist the data and being on variables is enough. For persisting data, you have CoreData, plists or save them on the sandbox.
Normally you could have a parent Controller, holding references to child Controllers. Each child should be independent and the parent would be the "glue" between them.
I can point you to this repo I created, called iOS Architecture, to help you understand how the controller and the model interact.
Here are some more points:
When your Action returns a View("SomeView"), it will check first if you have a layout, if not it will look for SomeView.cshtml in the Views folder of that controller.
If you want SomeView to talk to any Action in the controller, what you can do is to do a FormMethod.Post and assign the correct model and of course the Action name. That action name should be decorated with [HttpPost] or you can do a FormMethod.Get and decorate it with [HttpGet] or just leave it blank since [HttpGet] is default.
If you want another action redirecting first to another action without going to a page, you use RedirectToAction() instead of View and change the type to ActionResult instead of ViewResult
I hope it makes sense to you :)
So if there is some global state that every view of an MVC app will need to render ... for example: IsUserLoggedOn and UserName ... whats the appropriate way of getting that information to every view?
I understand that that part of the view should be in the master page or in a partial view thats added to the other views. But whats a good way to make sure the 'global' model data is passed to the view every time from all the relevant controllers and actions?
After asking this, I found this good blog post by Steve Sanderson:
http://blog.stevensanderson.com/2008/10/14/partial-requests-in-aspnet-mvc/
He suggests three different approaches:
Use a base controller class or ActionFilter to add the relevant model to the ViewData[] collection every time. I think a few people have suggested that sort of thing already.
Use Html.RenderAction() in the view ... and as he says:
If someone tells you that internal
subrequests are bad because
it “isn’t MVC”, then just bite them on
the face immediately. Also ask them
why they’re still willing to use Ajax,
and even <IMG> tags for that matter,
given that both are a form of
subrequest.
Use 'Partial Requests' (he provides the code for this on his blog) which allow one controller to nest calls to other controllers into a sortof nested ViewData structure.
codeulike - see the answer to a similar question asked at exactly the same time as this:
ASP.NET MVC displaying user name from a Profile
in a nutshell, basically create a base controller that's inherited by all your other controllers. the base controller then has an override function that carries thro' to all 'child' controllers. rather than duplicate the code, take a look at my answer above and give it a try..
cheers...
You could create base class for viewmodel which contains shared information and inherit that for each viewmodel.
public class ViewModelBase
{
// shared data
}
public class Page1ViewModel : ViewModelBase
{
// page 1 data
}
In masterpage you could use that base class
Inherits="System.Web.Mvc.ViewMasterPage<ViewModelBase>"
and in each view use those derived classes
Inherits="System.Web.Mvc.ViewPage<Page1ViewModel>"
I'm working on an ASP.NET MVC controller with several action methods, all of which need the same bit of data. This data requires a lookup that can only be done with the route values (so, I can't do the lookup in the constructor). I'm sure this has been discussed at length, but I've yet to find a satisfactory recommendation.
What's the best way to get this data without repeating myself in each action method? I'm working through:
Create an Action Filter, this seems like the best bet, but where do I store the object, in the action parameters? Is it appropriate to create a ViewModel object in an action filter and pass it directly to the action methods, for them to fill out the rest of the ViewModel object?
Create a child action (Html.RenderAction) to render this data, but this requires a second set of lookups since the child action requires another full cycle of instantiating the controller.
Helper method/property called in each action method.
Thought or opinions on a best approach here?
A filter is probably your best bet and you can store the object in ViewData.
Another option (not a better one) is to create your own controller base class that overrides the ExecuteCore method and does the lookup there.
I did this for logging since I want to log each page view and I didn't want to have to add a filter to each and every controller I made. In mvc 3 there will be a way to declare global filters which can fix this as well.
Ok, so given what you've told me, I would suggest using a custom ModelBinder.
It's the best fit for the situation. I would argue that using a filter is the wrong approach because a filter's job isn't to bind data - that's a job for a ModelBinder.
HTHs,
Charles
EDIT: I've just been thinking about it and I'm a little bit torn if you should use a model binder or not.
The general rule of thumb I just came up with is that if you need the ProjectDetails inside the action itself, use a ModelBinder but if you don't need the ProjectDetails inside the action, use an ActionFilter to just add it to your model / viewdata.
Maybe someone else could throw their 2c in.
You could override the OnActionExecuting() method in your controller and get the data there.
In my asp.net mvc project I store two objects in the session when the user logs in. One is a User object (representing the logged in user) and the other is the Organisation object (representing the user's organisation).
I need one or both of these objects to be available in every action method. What's the most elegant way of dealing with this? At the moment each action takes parameters for each of these objects, with a custom model binder retrieving the objects from session. This at least encapsulates the session access but it's annoying to have the params in every method signature. Is there a better way?
Here's what most of the action methods look like.
public ActionResult Pending(IUser CurrentUser)
{
var users = CurrentUser.GetMyOrgPendingUsers();
return View(users);
}
Since you need to access IUser in almost every action you can have a base controller from where every other controller is derived.
In the base controller put IUser as a member variable and override the
OnActionExecuting() method in the base controller, in which you can put the code to access the session variables.
OnActionExecuting() will be called every time a action is called.
The Controller class has a User property. Have you considered using this rather than designing your own way to track the current user?
I'd rethink using IPrincipal here--it is very handy and allows you to work your way into the rest of the .NET authentication bits very seamlessly.
As for the problem at hand, perhaps the easiest clean course of action would be to wire them into a base controller class as protected properties. Slightly more elegant would be to create a user context class, that takes a reference to Session (or better yet, whatever base interfaces you are using in the session) and expose that class from the controller. The main advantage there is it makes it easier to change the backing store if need be and lets one encapsulate more behavior in general.
I recently started to look into asp.net mvc. Here is my issue.
Say every page on an application needs a variable set by the user, e.g. a date. If the user starts from url I provide, it is all good as I ask for that date and save it for the session. How can I redirect the user to the first page if they save the some other url (to a different controller and action).
In other words, I guess I am looking for something like [Authorize] attribute but on an application level.
Thanks for any help.
I would probably create a base controller that all of my controllers derive from. In the base controller I'd override the OnActionExecuting method to check the session for the required variable. If the variable isn't present, I would set the ActionExecutingContext Result property to a RedirectToRouteResult to the appropriate controller/action to set the variable.
Another alternative is to create a custom FilterAttribute that you decorate the appropriate controllers/actions with that does basically the same thing. I would only do this if the filter was to apply only to certain controllers or actions and not all as you describe in your question.