Mvc viewpages and custom data for all pages - asp.net-mvc

I need some data to be available on all the viewpages inside the website. The data comes from an parameter supplied to all the routes, i want to get that param and get the according data for it and make it available for all the views (including the master pages).
It would be nice if it could be done in one place.
What do i need to do to get that functionality, can it even be done?
Greetz,
Richard.

The easiest (may not be the best) would be to write a base Controller class that
1) handles one of the following events to do the job:
OnActionExecuted
OnActionExecuting
OnResultExecuted
OnResultExecuting
2) Sets the data you want to have available in ViewData.
3) Use the ViewData from your views.
4) All your Controllers must inherit from your custom base Controller.
This might not be the nicest of all approaches as I usually try to avoid inheritance like the plague, but it will work. Other options.
1) Implement it in an ActionFilter and make sure add the attribute to all ActionMethods.
2) Use something like MVC Turbine to define ActionFilter's that trigger for all ActionMethods in you ASP.NET MVC Application.

Related

Sending a parameter to the controller in ASP MVC 2

I am writing an area for administering several subsites, almost all of the functionality will be the same across each site (add/edit/remove pages etc) and my repository on instantiation takes the SiteIdentity so all the data access methods are agnostic in relation to this. The problem I have at the moment is trying to make my action methods also agnostic.
The URL pattern I want to use is along the lines of:
"ExternalSite/{identity}/{controller}/{action}/{id}"
A naive approach is to have each action take the identity parameter, but this means having to pass this in to my repository on each action as well as include it in the ViewData for a couple of UI elements. I'd much rather have this something that happens once in the controller, such as in its constructor.
What is the best way to do this? Currently the best I can come up with is trying to find and cast identity from the RouteData dictionary but part of me feels like there should be a more elegant solution.
It sounds like you want to use OnActionExecuting or a Custom ModelBinder to do that logic each time you have a specific parameter name (also known as a RouteData dictionary key).
Creating a custom modelbinder in ASP.NET MVC
Creating an OnActionExecuting method in ASP.NET MVC, Doing Serverside tracking in ASP.NET MVC
You have access to your route values in Request.RequestContext.RouteData, so you can make base controller and public property SiteIdentity, in such case you can access it from all actions in all inherited controllers.

Attributes on Areas in ASP.NET MVC 2?

The "Admin" area in my app contains a bunch of controllers, and it's a bit repetitive to put an [Authorize] attribute on all of them. Is there a way of telling the framework that all controllers in a certain area should have certain attributes?
Edit: Inheritance is not a solution in this case. First of all the controllers already inherits from a custom class, and secondly, it should be about decorating the classes, not inheriting them.
MVC 3 has a new feature called Global Action Filters which would be perfect for what you are doing. Since you're probably not on MVC 3 yet, you can also implement Global Action Filter in earlier versions of MVC by following this example. Just customize the solution to filter check if you are in the "Admin" area for the currently executing request, then apply your Authorize attribute.
This will allow you to do this without having to apply a common base class as you requested.
You could create a base controller that all controllers in this area derive from and decorate it with the [Authorize] attribute.
There are four options,
Create a separate base controller and make admin sectionsu inherit from it
Add the Authorise Attribute to the controller class instead of each method / Actrion
Decorate each on individually
write your own logic for authorization and add that to your current base controller

ActionFilter or RenderAction in ASP.NET MVC?

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.

Modify a model in an ActionFilter

I have an actionfilter that I am running OnActionExecuting in ASP.NET MVC 2. Essentially I would like the actionfilter to sanitize my data and replace the current model (which will be passed to subsequent action filters and also my action method) with the sanitized model. Is this possible and is it a bad idea - if so why?
Thank you in advance,
JP
If you need to deal with your models, you're likely going to be dealing more within the scope of a single Controller (unless all your Controllers use the same model types?). An alternate approach would be to override the OnActionExecuting() and OnActionExecuted() methods of the Controllers themselves. This allows you to keep your business logic within the controller scope.
Generally ActionFilters are used for cross-cutting concerns - something that you want to run for many action methods, regardless of where they exist in the app. So unless your model sanitization logic applies across many controllers and actions, or is very generic (which perhaps it is, in which case your approach is probably good), you might want to bring it out of the filters and into your controllers. If it's something that can apply broadly, then an ActionFilter is just fine.
Here's for MVC v1, I hope this is not changed in v2:
var view = filterContext.Result as ViewResultBase;
if (view != null)
view.ViewData.Model ...
I don't see why you want to do it in OnActionExecuting, but if you must, do it there, and set some flag (private field) that OnActionExecuted has to tweak the resulting Model. But you'll have to use the latter anyway, except if you assign the .Result - in this case your action won't be called at all and the assigned result will be used.
BTW, for MVC 3 & 4:
filterContext.Controller.ViewData.Model

Rendering multiple views from multiple controllers on a single page

On the main page of my site, I would like to show several views which rely on their own controllers for data retrieval. I do not want to retrieve anything from the DAL in my Home controller.
For example, I want to show view listing top 5 news, a view with random quote from the database, another view with the users shopping cart contents, etc.
After Googling around, I found RenderAction method which is almost perfect, but it's not available in RC1, only in Futures, and apparently, it has some issues.
I found RenderPartial as well, but that relies on the main controller to pass data to the view.
Additional clarification:
The main reason I do not want data access logic in the Home controller is to avoid repeating the code and logic. I will use top 5 news view in several pages/controllers. I do not want to repeat data retrieval in every one of them. I already did separate a lot of logic and validation to business layer. The solution I'm after is RenderAction or UserControls as in classic ASP. I know i can use them in MVC as well, but... whats the point? I mean, if what i'm asking is too complicated or too absurd (reusable UI components), then MVC is definitely not for me, and I'd consider it seriously inferior to classic ASP.NET, because this requirement is really simple.
What you're asking is to basically not perform data access in the HomeController, this seems like a dogmatic approach. I would consider either using RenderAction from the Futures assembly (not sure what's wrong with it, I use it in a number of projects) or SubControllers from MvcContrib.
While I can understand the desire not to replicate functionality in multiple controllers, I don't understand the reluctance to have your Home controller interact with the DAL. I think the partial view is definitely the way to go. My solution to not replicating the functionality would be to push the code that generates the data for the various views into your business or data layer. You could then reference it from each of the required controller actions that use the partial views. Putting it in the business layer could isolate the controller from your data layer, if that's what you desire, but I still think it's the proper job of the controller action to obtain and provide the data to the view.
Another potential solution would be to populate the view generated by your Home controller via Ajax callbacks to the various controller actions that generate the required view components. The drawback to this is that it doesn't fail gracefully in the absence of javascript in the browser.
EDIT
Based on your clarification, I would suggest implementing a base controller that fills the ViewData for the shared controls in ActionExecuted (so that it's done only when the action succeeds). Derive your other controllers from the base controller when you want to inherit this behavior.
If you really don't want to use RenderAction, then the only other option you have is to load the necessary data pieces with action filters. Your home controller could then look like this:
public class HomeController : Controller
{
[RequireNews]
[RequireQuotes]
[RequireCart]
public ActionResult Index()
{
return View();
}
}
These action filters could be re-used where they are needed. You might also choose to place these on the controller class itself.

Resources