asp.net mvc2 - controller for master page and code organization - asp.net-mvc

I've just finished my first ASP.NET MVC (2) CMS. Next step is to build website that will show data from CMS's database. This is website design:
http://img56.imageshack.us/img56/4676/portal.gif http://img56.imageshack.us/img56/4676/portal.gif
#1 (Red box) - displays article categories. ViewModel:
public class CategoriesDisplay
{
public CategoriesDisplay() { }
public int CategoryID { set; get; }
public string CategoryTitle { set; get; }
}
#2 (Brown box) - displays last x articles; skips those from green box #3. Viewmodel:
public class ArticleDisplay
{
public ArticleDisplay() { }
public int CategoryID { set; get; }
public string CategoryTitle { set; get; }
public int ArticleID { set; get; }
public string ArticleTitle { set; get; }
public string URLArticleTitle { set; get; }
public DateTime ArticleDate;
public string ArticleContent { set; get; }
}
#3 (green box) - Displays last x articles. Uses the same ViewModel as brown box #2
#4 (blue box) - Displays list of upcoming events. Uses dataContext.Model.Event as ViewModel
Boxes #1, #2 and #4 will repeat all over the site and they are part of Master Page. So, my question is: what is the best way to transfer this data from Model to Controller and finally to View pages?
Should I make a controller for
master page and ViewModel class that will wrap all this classes together OR
Should I create partial Views for
every of these boxes and make each
of them inherit appropriate class
(if it is even possible that it
works this way?) OR
Should I put this repeated code in
all controllers and all additional
data transfer via ViewData, which
would be probably the worse way :) OR
There is maybe a better and more
simple way but I don't know/see it?
Thanks in advance,
Ile
EDIT:
If your answer is #1, then please explain how to make a controller for master page!
EDIT 2:
In this tutorial is described how to pass data to master page using abstract class: http://www.asp.net/LEARN/mvc/tutorial-13-cs.aspx
In "Listing 5 – Controllers\MoviesController.cs", data is retrieved directly from database using LINQ, not from repository. So, I wonder if this is just in this tutorial, or there is some trick here and repository can't/shouldn't be used?

To get data to my Master Page:
I don't like using an abstract class to get data to the master page. I prefer composition over inheritance.
I don't like to use the ViewData dictionary because it's not strongly typed.
I would create Views, ViewModels and Actions for each section. Then call Html.RenderAction(...) For example:
I would create CategoriesDisplay.aspx with only the html for the redbox. I would pass that your CategoriesDisplay model. Then in my controller:
public class CategoryController : Controller
{
public ActionResult DisplayCategories()
{
var model = new CategoriesDisplay();
...
return View(model);
}
}
Then in my Master Page:
<% Html.RenderAction<CategoryController>(c => c.DisplayCategoreis()); %>
This will render the CategoriesDisplay view inline within the Master Page. Which in turn allows you have SOC (Seperation of Concerns), clean and manageable code.

I fought with this as well. Initially I did a lot of dumping of extra data into the ViewData, which ended up having to be casted back (made some extensions that eased this, but still not great).
I would go with your choice #1 and make a ViewModel that wraps all of the classes you would need.

Related

Code reuse in multiple mvc views

I have read this link: https://www.future-processing.pl/blog/view-code-reuse-techniques-in-asp-net-mvc/
I can not use any of those helper ways...
I have to show on multiple mvc sites this string:
1612-1
That is an inquiry number: 16 is the day of month, 12 the month of year and 1 is the database id. I am sure that will not be the final impl but for now we take it as given.
public class MyViewModel
{
public string City { get; set; }
public string PostalCode { get; set; }
public List<string> ActionItemDescriptions { get; set; }
public string InquiryNumber { get; set; }
}
Where would you create the InquiryNumber?
If I put it inside the razor view I cant reuse it.
Seems business logic to me , so it belongs in the business layer.
Then, from within your controller you:
call the business component which returns the inquiry number
store the number in your view model
pass the view model to the view.
One way you could get an inquiry number, without using a helper, is this:
In a controller, have the following action method:
public ActionResult GetInquiryNumber()
{
// TODO : The code to get the inquiry number.
return Content("1612-1");
}
You can then call that method in any view you like, using the following:
#{ Html.RenderAction("GetInquiryNumber", "Home"); }
Obviously you will need to come up with your own method, and controller, names.
This isn't the ideal way of passing data to a view (using a viewmodel is preferable), but the above approach is an option to you.

Using the method in two model in one view of asp.net mvc 2.0

I have one view in my ASP.net MVC 2.0 project, I want to list the list of employee that I create method GetProfileCustomer() in CustomerModels and GetTransaction() in TransactionModels.
How can I import two different of models in a single view?
I'm fairly new to MVC as well, and I've struggled with the similar issues if I understand the question correctly.
I've found that you get much cleaner controller code if you design your ViewModels to be as close as possible to the data that the view is using. Your ViewModels can contain lists of other things including other model objects. Something like:
public class TransactionViewModel
{
public string dataelement1 { get; set; }
public int dataelement2 { get; set; }
//and so on...
//The Lists
public IList<Employee> EmpList { get; set; }
public IList<OtherModel> SomethingElse { get; set; }
//and so on...
}
In your controller, you construct and initialize your ViewModel
something like...
TransactionViewModel TVM = new TransactionViewModel();
//assign basic attributes here..
//make a list
TVM.Emplist = (from blah in context select blah).ToList();
//send it to the view
return View(TVM);
Hope this helps and happy to hear any feedback...

Display template not used for interface

I'm having a problem with display templates and dealing with interfaces and objects which implement the interface. In the example I have many objects, which I want to be rendered in a fixed way, I decided to create an interface and reference this in the view which I've decided to put into the shared display templates folder. DisplayFor doesn't seam to work for objects passed to it which implement the interface in the view, does any one know a solution to this.
Its probably easier to explain via code so I've wrote a quick example. The base interface and two classes which inherit from it:
public interface IPet
{
String Name { get; }
}
public class Dog : IPet
{
public String Name { get; set; }
}
public class Cat : IPet
{
public String Name { get; set; }
}
The example display template in shared display templates
#model IPet
<div>#Model.Name</div>
The example view model to be passed to the view
public class VM
{
public IPet Master { get; set; }
public IEnumerable<IPet> Minions { get; set; }
}
The controller (in this case to create mock information)
public ActionResult Index()
{
var viewModel = new VM();
viewModel.Master = new Cat(){Name = "Fluffy"};
var minions = new List<IPet>();
minions.Add(new Dog(){Name = "Dave"});
minions.Add(new Dog(){Name = "Pete"});
minions.Add(new Cat(){Name = "Alice"});
viewModel.Minions = minions;
return View(viewModel);
}
and finally the view which I would expect DisplayFor to work
#model ViewInheritance.Models.VM
<h2>Master</h2>
#Html.DisplayFor(x => x.Master)
<h2>Minions</h2>
#Html.DisplayFor(x => x.Minions)
Given that all the objects are are defined in the view model as the interfaces, howcome it fails to use the display template?
One solution I have found is to simply use the code
#Html.DisplayFor(x => x.Master, "IPet")
To recap, the question is:
Why does this happen?
Is there a way to make DisplayFor correctly work out that a type of Cat which implements IPet should in fact be looking at the common shared view IPet.cshtml?
Thanks
Starting a new MVC application and fixing the code to actually compile the view renders fine. It also renders fine when moving the view into the shared folder.
I Added setter to IPet:
public interface IPet
{
String Name { get; set; }
}
I updated implementation and added public accessors:
public class Dog : IPet
{
public String Name { get; set; }
}
public class Cat : IPet
{
public String Name { get; set; }
}
I left your VM alone and also did not change any code in your View.
Pressing F5, running the MVC application rendered the results as expected (See image).
Unfortunately, I don't think ASP.NET MVC currently supports automatically selecting templates based on implemented interfaces. I think this makes sense because a class could implement multiple interfaces, so if you had templates for more than one of those interfaces, which one should the framework choose?
You could use a base class instead of an interface if your design can cope with it:
Change IPet to a (possibly abstract) class.
Change IPet.cshtml to Pet.cshtml.
Otherwise I think you'll just need to explicitly tell the framework which template to use. Here are some options:
Decorate the view model properties with [UIHint].
Specify the template in your calls to your HtmlHelper methods such as DisplayFor.
Make your own ModelMetadataProvider and change the TemplateHint property of the resulting ModelMetadata.

Sending back collection of view models

I have not used asp.net mvc 3 for a while (got side tracked). I can't remember is it good practice to send back a collection of view models?
Right now how I have my code is I have a view model that contains other view models. This way I only ever send back one view model.
The view models in this one view model could be a collection though. However I have a case where I need to send multiple view models back but that's all I need to send. I don't have any other properties or other view models getting sent back.
It seems kinda pointless to wrap it around another view model just to make it only return one view model.
So is it good practice or not?
Thanks
There's absolutely nothing wrong with that approach. Pass back whatever is necessary to render the View. You can certainly refactor later if you require additional data to be passed to your View.
I only pass one ViewModel to a View. BUT, that single ViewModel might have some properties which are OTHER ViewModels.
here's a sample view model for my homepage....
namespace RavenOverflow.Web.Models.ViewModels
{
public class IndexViewModel : _LayoutViewModel
{
public IndexViewModel(ICustomIdentity customIdentity) : base(customIdentity)
{
}
public IList<Question> Questions { get; set; }
public AuthenticationViewModel AuthenticationViewModel { get; set; }
public IDictionary<string, short> RecentPopularTags { get; set; }
public UserTagListViewModel UserFavoriteTagListViewModel { get; set; }
public UserTagListViewModel UserIgnoredTagList { get; set; }
}
}
The 2nd, 4th and 5th properties all contain individual ViewModels.
I grabbed this from some tutorial code I wrote up recently.

Solid approach to loading reference data into view models in ASP.NET MVC

I want a way to separate the loading of reference data into a view model from the controller. At the moment I have a view model with a property for the selected value and the reference data:
public IEnumerable<SelectListItem> DayTypes { get; set; }
public int DayTypeId { get; set; }
and the data is populated from the relevant repository in the controller action:
model.DayTypes = _dayTypeRepository.GetAll().ToSelectList(d => d.Description, d => d.Identifier.ToString());
I would like to change this because it pollutes the controller with lots of repositories and code that is not core to its concerns. All of these dependencies make unit testing the controller a pain.
One possible approach to solving this would be to make the view model class do the loading which would require a custom model binder to instantiate them using the IoC container to provide the repository dependency. Is this a good option?
Another approach that I think would be good is hinted at in CodeCampServer but is incomplete and commented out involving attributes on the field in the view model:
[SelectListProvided(typeof(AllDaysSelectListProvider))]
public IEnumerable<SelectListItem> DayTypes { get; set; }
however I am struggling to figure out how this could be implemented in a way that would not require some major replumbing of the MVC framework.
How do you solve this problem?
EDIT: I want to keep with strongly typed views and avoid stuffing the data into view data.
FURTHER EDIT: I would also like a solution that is ideally model independent, by which I mean if the same reference data is needed by multiple view models this can be achieved with a single piece of code. Matt's approach is interesting but is tightly coupled to the view model.
I would use a service layer which would return me a POCO object that I would map to a view model. So my controller action would look like this:
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
var viewModel = Mapper.Map<Model, ViewModel>(model);
return View();
}
I also like using action filters to avoid the mapping code all over again so:
[AutoMap(typeof(Model), typeof(ViewModel))]
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
return View(model);
}
This way only the service talks with the CRUD repositories and the controller talks to the service and the mapping layer.
You could write a new ActionFilter that you can decorate an action method with; this action filter will load the reference data into the viewdata, which you can access from your view.
There is more on action filters here.
EDIT: Based on the users comments, this now includes a strongly typed option.
Firstly, you need to create the SharedViewModel to contain the shared data.
public class SharedViewModel
{
public List<string> Days { get; set; }
public List<string> Months { get; set; }
public List<string> Years { get; set; }
}
Next, we create the view model to be used by the Index view, which uses this shared view model.
public class HomeViewModel
{
public string ViewName { get; set; }
public SharedViewModel SharedViewModel { get; set; }
}
The next step is important, it implements an action filter called SharedData(), which will apply the shared data.
public class SharedDataActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var currentModel = ((HomeViewModel) filterContext.Controller.ViewData.Model);
currentModel.SharedViewModel = new SharedViewModel
{
Days = new List<string> {"Mon"},
Months = new List<string> {"Jan"},
Years = new List<string> {"2011"}
};
base.OnActionExecuted(filterContext);
}
}
At the moment, it just applies the whole shared data, but you can added parameters into the method to be selective.
When the action has been executed, this method takes the current model and adds the shared data.
Here is the controller action.
[SharedDataActionFilter]
public ActionResult Index()
{
return View("Index", new HomeViewModel { ViewName = "HomePage" });
}
You can access the data like any other strongly typed view, and the shared data wont affect the data already in the model (in this case "ViewName"). You can also use action filters across controllers, and globally across the site with mvc 3.
Hope this helps, Matt.

Resources