I'm looking for some guidance in terms of MVC architecture in terms of where to put the following logic.
My question is: Where should I put the logic to calculate the product rating?
I want to display some details about a product, and the rating in stars. The star rating is made up by the average user rating and an internal rating; weighted at 50% each.
Star Rating = 0.5(InternalRating) + 0.5(AverageUserRating)
I currently use the Product controller to populate 3 fields on the ViewModel:
#for (int i = 1; i <= Model.FullStars; i++)
{
<i class="full-star"></i>
}
#if (Model.HalfStar == true)
{
<i class="half-star"></i>
}
#for (int i = 1; i <= Model.EmptyStars; i++)
{
<i class="empty-star"></i>
}
At the moment I loop through the UserRating result in the Produt controller to get the average rating, and the logic to split into full and empty stars lies there.
The problem with my approach is this logic has to be duplicated in various controllers which call the product.
In terms of my understanding, a good method would be to create a class:
public class ProductStars
{
public int FullStars { get; set; }
public bool HalfStar { get; set; }
public int EmptyStars { get; set; }
}
Then create a DisplayTemplate for this class. Is this correct? And if so-- would I move the logic into a separate method in a utility class which accepts the product ID, gets the ratings from the database and populates the class?
Should the logic to populate the class then perhaps be moved into a table-valued function in SQL?
Thanks.
Personally, I would move the ratings logic into a business logic service and have that injected into the controller via dependency injection, then create a stars rating view helper to encapsulate your HTML used for creating the stars.
This way you've encapsulated your rating business logic that could theoretically be called via a website or web service, and you've also encapsulated your HTML generation for the rating view.
This article does a good job at demonstrating how to create a custom view helper.
I would definitely recommend trying to keep your controllers as skinny as possible as highlighted in one of rsenna's earlier comments. The controller should really only be used for handling the request and talking between the view and model.
Related
In my MVC project I have a Dashboard which is designed to enable a user to see multiple metrics courtesy of partial views backed by a model and brought into the main dashboard via #Html.Partial("_QuarterlyMetrics") and #Html.Partial("_TeamStats"). In total, there are about 7 UI sections dependent on other models outside of the #model DevProj.Dashboards.Models.DashboardModel. The question is, are partial views the best approach to multiple model dependency in a single view or is there a better performing maintainable approach? I've seen where some people create a parent ViewModel that takes an IEnumerable of all models a single view is dependent on.
You could create a container model specifically for your view. In which, you would store all necessary models. Alternatively, while not recommended, you could store your models in session and then load them in at run time.
Define your container...
public class CustomViewModelContainer
{
public XModel x { get; set; }
public YModel y { get; set; }
}
Then...
var container = new CustomViewModelContainer()
{
x = new XModel(),
y = new YModel()
};
return View("CustomView", container);
I have an Item model mapping to the DB like so:
public class Item
{
public int ItemId { get; set; }
public DateTime Created { get; set; }
public string Title { get; set; }
}
To display lists of these items, I have created a ItemSummaryViewModel like so:
public class ItemSummaryViewModel
{
public int ItemId { get; set; }
public string Title { get; set; }
public ItemSummaryViewModel(Item item)
{
this.ItemId = item.ItemId;
this.Title = item.JobTitle + " " + item.Created.ToString("ddd d MMM HH:mm");
}
}
I have also created a class to take a List< Item > and return a List< ItemSummaryViewModels > like so:
public class ItemSummaryViewModelList : List<ItemSummaryViewModel>
{
public ItemSummaryViewModelList(List<Item> items)
{
foreach (Item i in items)
{
ItemSummaryViewModel itemSummary = new ItemSummaryViewModel(i);
this.Add(itemSummary);
}
}
}
Finally, we use the controller to pass the list into the View like so:
public ActionResult Index()
{
//IEnumerable<ItemSummaryViewModel> itemsummaries = new IEnumerable<ItemSummaryViewModel>();
List<Item> ListOfItems = db.Items.ToList();
ItemSummaryViewModelList ListOfItemViewModels = new ItemSummaryViewModelList(ListOfItems);
return View(ListOfItemViewModels);
}
My Questions Are:
Is there a more efficient or "best practice" way of doing this?
To transform the list of DB models (Item) into a list of displayable View Models (ItemSummaryViewModels), we currently iterate through each item in the list and transform them individually. Is there a more efficient way of doing this ?
Essentially we're querying the DB and assigning the data to a ViewModel for display as a list. I can't help feeling that I'm "going round the houses" a bit and that there might be a more efficient or "best practice way of doing this.
Is there a better way?
Thanks
Try using LINQ select:
List<ItemSummaryViewModel> results = items.Select(
x =>
new ItemSummaryViewModel
{
ItemId = x.ItemId,
Title = x.Title + " " + x.Created.ToString("ddd d MMM HH:mm")
}).ToList();
Put that list in your view model.
Regarding efficiency, I would not worry until you have found that the simplest to implement solution was overly slow in practice. Get it working first and then only optimise when actually necessary. Obviously in the example you give, there are opportunities to only query and convert the subset of Items that the view requires (may it is all, but maybe you are paging?)
Structurally, I think the academic and professionally correct answer would be to have one set of objects to represent your database entities, a second set to represent the "domain" or business objects, and a third set to represent the all of the MVC models. However, depending the exact scenario this could be simplified:
If there is a really close mapping between the business objects and the database entities, and it is very unlikely that the database is going to change significantly, then you could have a single class for both.
If you have a very simple set of views that map very cleanly onto your business objects, then perhaps you can use business objects as your models. Unless your views do nothing but splat raw business objects onto a web page, I think your models will normally need to be more complicated than your current example though.
For that specific case, I would agree with #CorrugatedAir and say you could just use a plain List rather than create your own List class, and if want to be simpler, you could just use List and skip creating the ItemSummaryViewModel class too.
But try and be consistent throughout the application - so if you find a situation where your database entities can't be used as business objects, then it is best to have a separate set in all instances and have mappers between them.
To answer the "best practice" part of your question:
More efficient way (architecturally) will be to use Unit of Work and the repository patterns. That way you decouple your views from your data source, making it more reusable, more testable, more readable, hence more maintainable along with other "more"s.
The article is very graphical and gives you real feel of why do you need to tear apart database access from the controller.
To answer the technical part of how to transform it in a less verbose way,
I'd use something called AutoMapper. Using it, your complex transformation instead of the loop you presented will look as something like this:
public ActionResult Index()
{
var dbList = db.Items.ToList();
var vmList = Mapper.Map<List<Item>, List<ItemSummaryViewModel>>(dbList);
return View(vmList);
}
You will also have to put this initialization somewhere in your App_Start configuration (if MVC 4) or in Global.asax.cs file:
Mapper.CreateMap<ListOfItems , ItemSummaryViewModelList>();
Mapper.AssertConfigurationIsValid();
You can read more about why use AutoMapper and how to use it AutoMapper: Getting Started
Hope this helps!
I am using Entity Framework mapped to my database. I have a Basket model which can have many BasketItem models, and I have Promotions and Coupons models.
This is for eCommerce checkout functionality and I just don't understand how this will work, here goes:
Because my BasketItems have a foreign key relationship to the Basket if I want to sum up the Subtotal for my basket items in a partial class, I can do this:
public decimal Subtotal {
get {
return this.BasketItems.Sum(pb => pb.Subtotal);
}
}
This is helpful because I can use this inside a view, there's no mucking around with passing a DB context through and it's DRY, etc. etc.
Now I want to apply promotions or coupons to my Subtotal ideally I want it to look like this:
public decimal DiscountedSubtotal {
get {
decimal promotions_discount = 0;
decimal coupons_discount = 0;
return Subtotal - promotions_discount - coupons_discount;
}
}
But there is no access to Promotions or Coupons without either creating some crazy and unnecessary relationships in my database or some light hacking to get this functionality to work. I don't know what I should do to overcome this problem.
Solution 1:
public decimal DiscountedSubtotal(DatabaseEntities db) {
decimal promotions_discount = from p in db.Promotions
select p.Amount;
decimal coupons_discount = from c in db.Coupons
select c.Amount;
return Subtotal - promotions_discount - coupons_discount;
}
I don't want to use this in my View pages, plus I have to send through my context every time I want to use it.
Solution 2: (untested)
public List<Promotion> Promotions { get; set; }
public List<Coupon> Coupons { get; set; }
public Basket()
: base() {
DatabaseEntities db = new DatabaseEntities();
Promotions = db.Promotions.ToList();
Coupons = db.Coupons.ToList();
}
A bit of light hacking could provide me with references to promotions and coupons but i've had problems with creating new contexts before and I don't know if there is a better way to get me to the DiscountedSubtotal property I would ideally like.
So to sum up my question, I would like to know the best way to get a DiscountedSubtotal property.
Many thanks and apologies for such a long read :)
I think the problem here is that you're not really using a coherent architecture.
In most cases, you should have a business layer to handle this kind of logic. Then that business layer would have functions like CalculateDiscountForProduct() or CalculateNetPrice() that would go out to the database and retrieve the data you need to complete the business rule.
The business class would talk to a data layer that returns data objects. Your view only needs it's view model, which you populate from the business objects returned by your businesss layer.
A typical method might be:
public ActionResult Cart() {
var model = _cartService.GetCurrentCart(userid);
return View(model);
}
So when you apply a discount or coupon, you would call a method like _cartService.ApplyDiscount(model.DiscountCode); then return the new model back to the view.
You might do well to study the Mvc Music Store sample project, as it includes cart functionality and promo codes.
http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-1
I have a Service class with a method called GetProducts(). That encapsulates business logic and calls the repository to get a list of products.
My MVC view wants to show that list of products as an MVC SelectList. Where is the correct place for that logic to go. I seem to have 3 options:
Model
The Model should expose a property called ProductSelectList. When the getter of this property is called by the View, the Model should call Service.GetProducts() and convert the result to a SelectList before passing it on.
Plausible argument: The Model should make calls to business logic and the repository. The View should merely render predetermined data. The Controller should not be involved, save for passing contextual data to the Model.
View
The View should contain code that calls Service.GetProducts() directly and converts the result to a SelectList inline.
Plausible argument: The View should call for this data directly as it is specifically for use on the View. There is no need to involve the Model or Controller, as we are calling an abstracted Service method anyway, so anything else just adds extra overhead.
Controller
The Controller should make the call to Service.GetProducts(), convert the results to a SelectList and pass it through to the Model, which should contain a simple ProductSelectList property. The View will access this property for rendering.
Plausible argument: The Controller knows which parameters to provide to the Service method, so it should make the call. The Model should be a simple placeholder for data, filled by the Controller. The View's job is to simply render the data from the Model.
I have a feeling that the correct answer is Model, but the other two make some reasonable points. Perhaps I've muddied the waters by already having a Service class that's separate to the Model?
Would anybody care to share their opinion? Is this just a matter of taste?
I personally subscribe to the logic of Number 3, allowing the controller to populate the Model (or View Model as is sometimes differentiated).
I have my views dumb and only displaying data.
I have my View Models store the information that the View will need, occasionally exposing 'get only' properties that format other properties into a nicer format. If my model needs access to my services, then I feel I'm doing something wrong.
The controllers arrange and gather all the information together (but do no actual work, that is left for the services.
In your example, I would have my controller action similar to:
public ActionResult Index()
{
IndexViewModel viewModel = new IndexViewModel();
viewModel.ProductSelectList = new SelectList(Service.GetProducts(), "Value", "Name");
return View(viewModel);
}
and my view model similar to:
public class IndexViewModel()
{
public SelectList ProductSelectList { get; set; }
public int ProductID { get; set; }
}
With the appropriate part of the view looking like:
#Html.DropDownListFor(x => x.ProductID, Model.ProductSelectList);
This way I'm content that I know where to look if there is an issue with anything and everything has a very specific place.
However, there is no correct way as seems always to be the case with these things. Stephen Walther has a good blog series on MVC tips. In one he talks about the View Model emphasis and although not a SelectList he populates, the SelectList is still data in much the same way his list of products is.
In a classic MVC architecture your Model shouldn't be much more than a container for your view data hence the reason it's often called a ViewModel. A ViewModel is different from the Entity Model(s) that your service layer manages.
Your controller is then responsible for populating your ViewModel from the entity model(s) returned by your service layer.
Due to convenience some developers will use their service layer entities directly in their ViewModels but long term that can lead to headaches. One way around that is to use a tool such as AutoMapper to automate the shuffling of data to and from your ViewModel and entity models.
Here's what a controller might look like. Notice that data such as the SSN does not get exposed to the view since there is a mapping from your Entity Models to your View Model.
public class Customer : IEntity
{
public string CustomerID { get; set; }
public string SSN { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
}
public class CustomerEditViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Country { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string PhoneNumber { get; set; }
}
public class CustomerController
{
[AcceptVerbs (HttpVerbs.Get)]
public ActionResult Edit ()
{
Customer customer = _customerService.GetCustomer (User.Identity.Name);
var model = new CustomerEditViewModel ()
{
FirstName = customer.FirstName,
LastName = customer.LastName,
Address1 = customer.Address.Address1,
Address2 = customer.Address.Address2,
Country = customer.Address.Country,
City = customer.Address.City,
State = customer.Address.State,
Zip = customer.Address.Zip,
PhoneNumber = customer.Address.PhoneNumber,
};
return View (model);
}
}
You're right that there are a number of ways to handle this, and that's even before considering variations like MVP, MVVM, et cetera. Since you're asking about ASP.Net MVC in particular, I will defer to Microsoft:
An MVC model contains all of your application logic that is not
contained in a view or a controller. The model should contain all of
your application business logic, validation logic, and database access
logic. For example, if you are using the Microsoft Entity Framework to
access your database, then you would create your Entity Framework
classes (your .edmx file) in the Models folder.
A view should contain only logic related to generating the user
interface. A controller should only contain the bare minimum of logic
required to return the right view or redirect the user to another
action (flow control). Everything else should be contained in the
model.
In general, you should strive for fat models and skinny controllers.
Your controller methods should contain only a few lines of code. If a
controller action gets too fat, then you should consider moving the
logic out to a new class in the Models folder.
Source
I would say your call belongs in the Model.
One thing to keep in mind is that the SelectList class is specific to MVC only. So in my opinion it shouldn't be included in any business logic, and model classes fall into that category. Therefore your select list should be a part of a view model class instead.
This is the way it works in my projects:
Controller method is called
Controller uses repository (business logic, in other words) to get model data
Controller converts the model data if necessary and creates a view model object
Controller passes the view model to the view
The view displays the data in the view model with limited logic to show or hide things, etc
I'd go with option 3. In general, I'll construct my MVC apps such that the controller makes a call to the service to return a model (or collection of models) which are then passed to the view.
I generally keep my models very thin. They are a flattened representation of the data with validation attributes and that's it. I use a service (or model builder) layer to construct the models and do business logic on them. Some folks embed that into the model, but I find that makes for a messy project.
You definitely don't want the view making any calls to your services.
Update...
I'm assuming that this SelectList is your model. If instead it's a part of your model, then you're right, you should put it in your model. I generally don't like to make it a method call, though. I'd have a property on my model:
public SelectList Products { get; set; }
And have my service or model builder class actually populate it. I don't usually have any data-oriented methods on my models.
I'm going with option 1.
Models are the place to make calls to business logic, et cetera.
View - Should display only what the ViewModel already has been populated with.
Controller - the job of the Controller is to direct the traffic coming in (from Web requests) to the Logic that is responsible for handling the request. Hence the term 'controller'.
There are always exceptions to these, but the best place (structurally) is the Model.
I had this problem when I started into MVC and came up with this solution.
The controller talks to a Service Layer. The service layer contains my Domain models and does all the processing for request from the Controllers. The service layer also returns ViewModels to satisfy requests from the controller.
The service layer calls a repository and gets the entities it will need to build the ViweModels. I often use Automapper to populate the ViewModel or collections within the view model.
So, my view models contain all that is needed by the View, and the Controller is doing nothing but handling request and forwarding them to the appropriate service handler.
I don't see a problem with having view specific items like SelectLists in the view Model either.
None of the above.
In my web layer I basically just have html and javascript views. The model shouldn't leak into the view and neither should the services.
I also have an Infrastructure layer which binds the services and model to the views. In this layer there are ViewModels, which are classes that represent what will be displayed on the screen, Mappers, which do the work getting data from services/model and mapping it to the view model, and Tasks, which perform tasks such as Saving, Updating and Deleting data.
It is possible to put a lot of this infrastructure in the Controllers, similar to the example Todd Smith has given above, but I find for anything other than trivial views the Controller becomes littered with code to load data and populate view models. I prefer a dedicated single responsibility mapper class for each view model. Then my controller will look something like
public class CustomerController
{
[AcceptVerbs (HttpVerbs.Get)]
public ActionResult Edit (int id)
{
return View (CustomerEditMapper.Map(id));
}
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult Save(CustomerEditViewModel model)
{
var errors = CustomerEditUpdatorCommand.Execute(model);
ModelState.AddErrors(errors);
return View ();
}
}
I'm torn between option 1 and option 3. I've ruled option 2 out completely as to me that's polluting the view with procedure calls not just presentation layer work.
Personally I would do it in the model and the getter would call the Service layer but I also subscribe to the belief that the model should only contain the information the view needs to render the page, by not fully containing the data in the model at the time you pass it to the view you are breaking this.
Another option here though would be to avoid tightly coupling the view and model by putting a Dictionary of the Products into the view through a Service Call then using the view to transform the Dictionary to a SelectList but this also gives you the ability to just output the information as well.
I think this boils down to a preference as to where you are happy having your logic.
This came up during one of our retrospectives and wanted some additional feedback and a spot check. We currently have a number of views that enable/disable based on boolean flags (Model.IsNew is an example). I'm of the opinion that views should be as simple as possible and controllers should determine the data for that view, not necessarily how it works. I think views, partial or full, should be -told- what to do and handle that vs the view determining what should be shown/hidden. A very basic example goes as follows but covers both sides of this and is mostly a reflection of what we have ...
A controller has a pair of methods (post/get) called Details. [Get]Details has a single parameter, Id and [Post]Details takes id and a viewmodel. Within the post, the method is ~30 lines long checking for valid model, determining if its new, if a certain value changed (triggers redirect) and so on (I think this is incorrect). The [Get]Details check for empty id, populates the necessary dropdowns, nothing fancy (I think this is right). The detail view itself contains a small bit of logic : If (!Model.IsNew) { RenderAction(History => History.Show(id); } (I think this within an if is incorrect, the Show should know what to display, regardless if its new). The plus to this is the layout for said Detail view isn't done twice. Details/Add would be nearly identical, minus some disabled fields depending on state (maybe these should be partials?) -- the entity could be disabled/deleted making the values editable or not.
Thoughts, opinions, insights?
why not create multiple Views and Actions?
Details
Edit
[HttpPost]
Edit
Create
[HttpPost]
Create
Then they could share a Model object
public ThingModel
{
public Thing Thing { get; set; }
}
or have the Create and Edit actions use a safer (prevent html injection) model which will also let you use the Validation options built in.
public ThingEditorModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public bool IsNew { get { return Id == 0; } }
}
And then for Edit and Create you can create an EditorTemplate (Shared/EditorTemplates/ThingEditor.ascx) that the Create and Edit could share
I think you're on the right track...use the 'main' view for layout and then use Templated Helpers for the 'logic'. Lean on Html.DisplayFor(x=>x.Thing) and EditorFor
You definately don't want the layout in two places.