Where to put master page's code in an MVC application? - asp.net-mvc

I'm using a few (2 or 3) master pages in my ASP.NET MVC application and they must each display bits of information from the database. Such as a list of sponsors, current fundings status etc.
So my question was, where should I put these master-page database calling code?
Normally, these should goes into its own controller class right? But then that'd mean I'd have to wire them up manually (e.g. passing ViewDatas) since it is out of the normal routing framework provided by the MVC framework.
Is there a way to this cleanly without wiring ViewData passing/Action calls to master pages manually or subclassing the frameworks'?
The amount of documentation is very low... and I'm very new to all this including the concepts of MVC itself so please share your tips/techniques on this.

One way to do this is to put in the masterpage view the hook for the ViewData and then you define a BaseController : Controller (or multiple base classes) where you do all the db calls you need.
What you wanna do is quite the same thing described in this articles.
I hope this helps!
Regards

Great question. You have several options available to you.
Have a jQuery call on your masterpage that grabs the data you need from a controller and then populate your fields using jQuery again.
Your second option is to create user controls that make their own calls to the controller to populate their information.
I think the best choice is creating controls for the region of your masterpage that has data that needs to be populated. Thus leaving your masterpage to strictly contain design elements. Good luck.

If you don't mind strongly typed view data, you can put all the master page data in a common base class for viewData. You can set this data in the base class's constructor. All your views requiring additional data will then need strongly typed viewdata that inherits from this base class.
To allow a call to View() in your controllers without any explicit viewdata you can override View in your ControllerBase:
protected override ViewResult View(string viewName, string masterName, object model)
{
if (model == null)
{
model = new ViewDataBase();
}
return base.View(viewName, masterName, model);
}

Related

ASP.NET MVC: How to handle model data that must go to every view?

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>"

ASP.net MVC - How to persist model over various views

Situation: In some project management software written in asp.net I have a create project page (working fine). I need to add to this the ability to add tasks from a list of templates to this project pre-creation BUT the list of available tasks is dependent on some values sitting in the create form.
My abstract solution is this:
I have a "Create" view and an "Add Tasks" View - both strongly typed to a composite viewModel defined in the controller
My Create method checks which button was used to call it - if the
button was "Add Tasks" it then renders the AddTasks view, passing the model in from the create view, again all in the same controller.
The AddTasks View posts to the Create view with one of two buttons, one loads the view and the other causes an actually DB save.
My Problem is this:
The different views use different properties of the same model, but in passing this model between them, the data is reset (in any case reload or save).
I am guessing this is happening from auto binding of data - though I thought fields not present on the form would not overwrite existing model data passed down.
There is hardly any code in the controller manipulating the model at present - It is only passed from view to view in these cases.
This is the controller code:
// POST: /Project/Create/<viewModel>
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id,id")] ProjectViewModel model)
{
if (model.SubmitValue == "Create")
{
try
{
model.Project.Id = Guid.NewGuid();
model.Save(this.User.Identity.Name);
return this.RedirectToAction("Details", new {id = model.Project.Id});
}
catch (Exception e)
{
this.ModelState.AddModelError(e.ToString(), e.ToString());
}
return View(model);
}
if(model.SubmitValue == "AddTasks")
{
return this.View("AddTasks",model);
}
return this.View(model);
}
//POST: /Project/AddTasks/ + model
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddTasks([Bind(Include = SelectedCarrierTasks")]ProjectViewModel model)
{
return View(model);
}
The Question is: How do I maintain the state of the model across these views until it finally save it?
I would prefer to avoid any hackish (TempData) or JS dependant solutions, but I am not closed to these if they are really the best solution.
Thanks,
Adam Tolley
One simple solution is to persist the ViewModel object in a Session variable and bind the View from this source.I ts certainly not the most elegant solution. Another option, and probably less elegant one is persist this model data in the database, with some temporary/unsaved flag.
The problem is that when you display the add tasks view you're not providing fields for your "Project" object therefore the ModelState loses the data related to the project, you will need to provide this fields to ensure you're not loosing that data.
You don't need to display this fields they can be of type hidden and they will preserve the value. Just make sure that if you will be binding to a view model you will need to name this fields correctly like this Model.Project.Property.
Perhaps I am trying to solve the wrong problem (ala Bruce Eckel). I am going to try to move to a structure that needs this sort of fuzzy boundary less. I don't want to adopt a REST paradigm only to shoe-horn it into a stateful application.
Possibly these controls belong on the same page, and I can use some JQuery goodness to put in a tab pane for easiness on the eyes.
Thanks to those who answered, I found each useful and will try to remember to up-vote them as soon as I have some more rep.
I can't comment on other peoples questions at the moment, but the only real option is the session if you want to persist an objects state during web requests, or serializing it and placing it in a hidden field.
Or a final option would be to change the way your pages work so you can save the object after each request...
If your using nHibernate then you might want look into the Conversations pattern, but this just essentially saves the nHibernate session into the asp.net session anyway...

ModelFactory in ASP.NET MVC to solve 'RenderPartial' issue

The 'RenderPartial()' method in ASP.NET MVC offeres a very low level of functionality. It does not provide, nor attempt to provide a true 'sub-controller' model *.
I have an increasing number of controls being rendered via 'RenderPartial()'. They fall into 3 main categories :
1) Controls that are direct
descendants of a specific page that
use that page's model
2) Controls that are direct
descendants of a specific page that
use that page's model with an
additional key of some type.
Think implementation of
'DataRepeater'.
3) Controls that represent unrelated
functionality to the page they appear
on. This could be anything from a
banner rotator, to a feedback form,
store locator, mailing list signup.
The key point being it doesn't care
what page it is put on.
Because of the way the ViewData model works there only exists one model object per request - thats to say anything the subcontrols need must be present in the page model.
Ultimately the MVC team will hopefully come out with a true 'subcontroller' model, but until then I'm just adding anything to the main page model that the child controls also need.
In the case of (3) above this means my model for 'ProductModel' may have to contain a field for 'MailingListSignup' model. Obviously that is not ideal, but i've accepted this at the best compromise with the current framework - and least likely to 'close any doors' to a future subcontroller model.
The controller should be responsible for getting the data for a model because the model should really just be a dumb data structure that doesn't know where it gets its data from. But I don't want the controller to have to create the model in several different places.
What I have begun doing is creating a factory to create me the model. This factory is called by the controller (the model doesn't know about the factory).
public static class JoinMailingListModelFactory {
public static JoinMailingListModel CreateJoinMailingListModel() {
return new JoinMailingListModel()
{
MailingLists = MailingListCache.GetPartnerMailingLists();
};
}
}
So my actual question is how are other people with this same issue actually creating the models. What is going to be the best approach for future compatibility with new MVC features?
NB: There are issues with RenderAction() that I won't go into here - not least that its only in MVCContrib and not going to be in the RTM version of ASP.NET-MVC. Other issues caused sufficent problems that I elected not to use it. So lets pretend for now that only RenderPartial() exists - or at least that thats what I've decided to use.
Instead of adding things like MailingListSignup as a property of your ProductModel, encapsulate both at the same level in a class like ProductViewModel that looks like:
public class ProductViewModel() {
public ProductModel productModel;
public MailingListSignup signup;
}
Then get your View to be strongly-typed to the ProductViewModel class. You can access the ProductModel by calling Model.productModel, and you can access the signup class using Model.signup.
This is a loose interpretation of Fowler's 'Presentation Model' (http://martinfowler.com/eaaDev/PresentationModel.html), but I've seen it used by some Microsoft devs, such as Rob Conery and Stephen Walther.
One approach I've seen for this scenario is to use an action-filter to populate the data for the partial view - i.e. subclass ActionFilterAttribute. In the OnActionExecuting, add the data into the ViewData. Then you just have to decorate the different actions that use that partial view with the filter.
There's a RenderPartial overload I use that let's you specify a new ViewData and Model:
RenderPartial code
If you look at the previous link of the MVC source code, as well as the following (look for RenderPartialInternal method):
RenderPartialInternal code
you can see that if basically copies the viewdata you pass creating a new Dictionary and sets the Model to be used in the control. So the page can have a Model, but then pass a different Model to the sub-control.
If the sub-controls aren't referred directly from the main View Model, you could do the trick Marc Gravell mentions to add your custom logic.
One method I tried was to use a strongly typed partial view with an interface. In most situations an agregated ViewModel is the better way, but I still want to share this.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IMailingListSignup>" %>
The Viewmodel implements the interface
public class ProductViewModel:IMailingListSignup
Thats not perfect at all but solves some issues: You can still easily map properties from your route to the model. I am not shure if you can have a route parameter map to the properties of MailingListSignup otherwise.
You still have the problem of filling the Model. If its not to late I prefer to do it in OnActionExecuted. I dont see how you can fill a Model in OnActionExecuting.

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.

passing viewdata to asp.net mvc masterpages

I'm trying to pass ViewData to my asp.net mvc masterpage for an mvc usercontrol that I keep on a masterpage. For example, I created a dropdownlist of names as an mvc usercontrol and I put that in my masterpage.
The problem I am running into is passing the ViewData to the masterpage. I found this article from Microsoft which has a decent solution but I was wondering if there are other "better" solutions out there. The thing I don't like about the solution in the link is that I have to change every controller to inherit from a new controller class.
http://www.asp.net/learn/MVC/tutorial-13-cs.aspx
Edit: The problem I am looking at is the fact that if I place a user control in my masterpage that relies on ViewData, I have to REPEATEDLY include that ViewData for every single page that uses said masterpage. It's possible the solution in the link above is the best solution but I was hoping there were other alternatives.
The master page already has access to the ViewData. If you want strongly typed access to it, you need to do two things:
Put the master page stuff in a base class (e.g. CommonViewData)
Have you master page inherit from the generic ViewMasterPage<> class:
" %>
Could you possibly use the OnActionExecuting method on a base controller class and populate the view data there?
Something like:
protected override void OnActionExecuting(ActionExecutingContext context)
{
context.Controller.ViewData.Model = GetDataForControl();
}
I haven't tried it so it's just a thought...
For what it's worth, I am using the method from that tutorial in a current project and it works very well.
What you can also do, if it is data that is somewhat static (like a menu that doesn't change much), is to put the object on the cache so your database isn't called for every controller initialisation.
I usually use an abstract controller class for my MasterPage, it is the best solution, because the MasterPage is like an "abstract view". But I override the MasgerPageController View() method to include the viewdata.
protected override ViewResult View(string viewName, string masterName, object model)
{
this.ViewData["menu"] = this.PagesRepository.GetPublishPages();
return base.View(viewName, masterName, model);
}
I don't quite get your problem...
The problem I am looking at is the fact that if I place a user control in my masterpage that relies on ViewData, I have to REPEATEDLY include that ViewData for every single page that uses said masterpage.
Well yeah... of course you do. If you have a usercontrol in your master page then of course you're going to have to pass the required data for that usercontrol for every action & view that uses that masterpage.
It's not like you have to repeat yourself if you are just inheriting from a base controller.
Is your issue the fact that some controllers have actions that both do and don't call views that derive from that particular masterpage? So therefore if you are implementing a base controller, the actions that don't use that particular masterpage will still have the viewdata for it...? (If all that makes sense ;-)
I think the solution suggested does work but not the ideal solution. If we put the code in the BaseController constructor it is going to be called even for Action methods which does not have a MasterPages (e.g Post methods and Ajax methods).
I think a better solution(not ideal) is to call Html.Action method in the Master page.

Resources