Which code is more of a correct MVC approach? Put code in controller or ViewModel? - asp.net-mvc

I have read that in an MVC application one should keep controllers "thin". But when I put code to fetch data in the ViewModel I feel it's less intuitive to locate, meaning when I am troubleshooting I generally tend to look in my controller first (or maybe that's my real problem). Also I find one can reuse the same VM for many different things if you pass data through the controller.
Am I violating some big principle or causing performance issues?
For example, compare these dummy snippets with two approaches, both seem to work just fine:
Assume a Repository called repositoryy with a method GetCourses() that fetches a list of Courses.
1) ViewModel fetches the data and controller directs traffic:
public CourseViewModel
{
private MyProjectEntities db = new MyProjectEntities();
Repository repository = new Repository();
{
public CourseViewModel()
{
Courses = db.Course.ToList();
}
public List<Course> Courses {get; set;}
}
}
public class CourseController : Controller
{
public ActionResult Index()
{
var courseviewmodel = new CourseViewModel();
return View(courseviewmodel);
}
}
2) Controller fetches data, passes to ViewModel and then to the View:
public CourseViewModel
{
public List<Course> Courses {get; set;}
}
public class CourseController : Controller
{
public ActionResult Index()
{
var courseviewmodel = new CourseViewModel();
courseviewmodel.Courses = repository.GetCourses.ToList();
return View(courseviewmodel);
}
}

The second option. Btw there's no 'more correct MVc approach'. There is the MVC separation and that's that. Your first option breaks that separation, because the view model does the controller's work instead of being the 'dumb' dto holding the view's data. You don't want to couple the view model to the model.
In MVVM though, the view model acts a bit like a controller, however that approach is best suited for desktop aps, not web apps.

Related

How to convert DTO to View Model and then back again? [duplicate]

This question already has answers here:
Where to convert business model to view model?
(3 answers)
Closed 5 years ago.
I'm using MVC 4 with the repository pattern and unit testing also. I have a typical controller that has simple CRUD functionality. I've separated my View Models from my DTOs and I would like to know the best way to convert between the 2:
Models:
I have Admin.Models.Product which is my view model and AdminAssembly.Models.Product which is my DTO.
Controller:
//repo that handles product operations
AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> db;
//default constructor
public ProductController() { db = new AdminAssembly.Repositories.EntityRepo<AdminAssembly.Models.Product>(new AdminAssembly.Models.EntitiesContext()); }
//unit testing constructor
public ProductController(AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> context) { db = context; }
//
// POST: /Product/Create
[HttpPost]
public ActionResult Create(Admin.Models.Product product) {
if (ModelState.IsValid) {
//COMPILE-ERROR: how to convert to DTO?
db.Add(product);
}
return View();
}
//
// GET: /Product/Edit/5
public ActionResult Edit(int id) {
//COMPILE-ERROR: how to convert to view model?
Admin.Models.Product product = db.GetAll().Where(p => p.ID == id);
return View(product);
}
How do I convert between the 2?
Do I reference my DTO assembly in my view model and do something like: (won't this break my unit testing?)
//convert to AdminAssembly.Models.Product
db.Add(product.ToDTO());
//convert back to Admin.Models.Product via constructor
Admin.Models.Product product = Admin.Models.new Product(db.GetAll().Where(p => p.ID == id));
Do I need some sort of object conversion black box?
Converter.ToViewProduct(product);
Some sort of interface?
or something else?
Update 1:
public static class Product {
public static Admin.Models.Product ToView(AdminAssembly.Models.Product dto) {
Admin.Models.Product viewProduct = new Admin.Models.Product();
//straight copy
viewProduct.Property1 = dto.Property1;
viewProduct.Property2 = dto.Property2;
return viewProduct;
}
public static AdminAssembly.Models.Product ToDTO(Admin.Models.Product viewModel) {
AdminAssembly.Models.Product dtoProduct = new AdminAssembly.Models.Product();
//straight copy
dtoProduct.Property1 = viewModel.Property1;
dtoProduct.Property2 = viewModel.Property2;
//perhaps a bit of wizza-majig
dtoProduct.Property1 = viewModel.Property1 + viewModel.Property2;
return dtoProduct;
}
}
The long-hand response
[HttpPost]
public ActionResult Create(Admin.Models.Product product)
{
if (ModelState.IsValid)
{
//COMPILE-ERROR: how to convert to DTO?
var dtoProduct = new AdminAssembly.Models.Product();
dtoProduct.Property1 = product.Property1;
dtoProduct.Property2 = product.Property2;
//...and so on
db.Add(dtoProduct);
}
return View();
}
While this looks verbose and tedious (and it is) it has to happen eventually, somewhere.
You can hide this mapping either in another class or extension method, or you can use a third party like AutoMapper, as Charlino points out.
As a side note, having two classes with the same name in two different namespaces will eventually get confusing (if not for you, then for the next person who has to maintain your code.) Implement friendlier and more descriptive names wherever possible. For example, put all your view models in a folder called ViewModels, not Models. And append all your view models with ViewModel, or VM. It's also a good convention, imo, to name your view models based on the view that they are for, not so much the domain model that they will be mapped to, as not all view models will map directly to a domain model. Sometimes you'll want parts of more than one domain model, for a single view, and that will blow up your naming convention.
So in this particular case I would suggest changing Admin.Models to Admin.ViewModels and then rename the view model version of Product to CreateViewModel. Your code will be much more readable and will not be littered with namespaces throughout your methods.
All of that would result in a method that would look more like this:
[HttpPost]
public ActionResult Create(CreateViewModel viewModel)
{
if (ModelState.IsValid)
{
var product = new Product();
product.Property1 = viewModel.Property1;
product.Property2 = viewModel.Property2;
//...and so on
db.Add(product);
}
return View();
}
Check out a library called AutoMapper.
From their wiki:
What is AutoMapper?
AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another. This type of code is rather dreary and boring to write, so why not invent a tool to do it for us?
If you dont want to use AutoMapper you may use extensions, as suggested by #Forty-Two. If the number of things to map is no very great, I would go with this approach, just because then, AutoMapper == YAGNI
public static class Extensions
{
public static ViewModel ToViewModel(this Model )
{
var vm = new ViewModel()
{
//map
};
return vm;
}
public static Model ToModel(this ViewModel viewModel)
{
var model = new Model()
{
//map
};
return model;
}
}
Similar to your code in UPDATE, but using extensions instead.

mvc best practice for common code

I have an MVC application. The following code is being used in multiple places within a controller and in multiple controllers. I would like to have this code in one place and call it from each location. What's the best way to do that in MVC?
The code below gets a row from the database and creates ViewData that can be read from the view. With webforms, I would create a public sub within a class and pass the year and month values. Is there a way this code could become part of the model?
var monthlyexpenseincome = (from vu_monthlyresult in dbBudget.vu_MonthlyResults
where vu_monthlyresult.Month == defaultmonth && vu_monthlyresult.Year == defaultyear
select vu_monthlyresult).Single();
var yearlyexpenseincome = (from vu_yearlyresult in dbBudget.vu_YearlyResults
where vu_yearlyresult.Year == defaultyear
select vu_yearlyresult).Single();
ViewData["MonthlyExpenses"] = monthlyexpenseincome.Expenses;
ViewData["MonthlyIncome"] = monthlyexpenseincome.Income;
ViewData["MonthlyProfit"] = monthlyexpenseincome.Income - monthlyexpenseincome.Expenses;
Generally, If you have common code across multiple controllers, you can create another class which inherits from Controller and keep your methods there and let your indidual Controlellers inherit this new class
public class BaseController : Controller
{
protected string GetThatInfo()
{
//do your magic logic and return some thing useful
return "This is demo return.Will be replaced";
}
}
And now you can inherit from this for your other controllers
public class UserController: BaseController
{
public ActionResult Index()
{
return VieW();
}
}
But In your case, The data you are taking is something specific to your Domain data. so i would suggest you to move it to a different class ( like a new Service / Business Layer)
public static class ProfitAnalysis
{
public static decimal GetTotalExpense()
{
//do your code and return total
}
}
And you can call it from wherever you wanted like
decimal totalExp=ProfitAnalysis.GetTotalExpense();
And You will soon realize, Having so much ViewData usage is making your code difficult to read and Maintain. Do not wait for that day. Switch to strongly typed classes to pass data.
You should place your queries in a "business layer", which is just a class that you call to do the business logic. Then you can reuse it in any place you like, just instantiate the business class and use it. You could also make the methods static if they don't require state, then you wouldn't have to even instantiate it.
For example:
var expenseService = new expenseService();
ViewData["MonthlyExpenses"] = expenseService.GetMonthlyExpenses();

ASP.NET MVC - Service layer, single or many services in each controller action?

I'm starting to implement a service layer to my MVC project to thin down some bloated controllers (it also has repository / unitofwork pattern).
My question is if you have a complicated view model for a page with lots of child objects etc, and quite a lot of logic going on behind the scenes (to give you an idea the controller the original developer wrote had almost 4000 lines of code!!) is it OK to have multiple services going off doing their thing? or should I just have one big ReportService which does everything?
My controller is starting to look like this? and if I carry on I could end up having quite a lot of different services being called to build up the view model.
Does this look OK or is it starting to go in the wrong direction?
public ViewResult Index(int? reportId)
{
// get the base report object
var reportService = new ReportService();
var report = reportService.GetByReportId(reportId);
var model = Mapper.Map<Report, ReportViewModel>(report);
// get the current active user
var userService = new UserService();
var user = userService.GetCurrentUser();
model.User = Mapper.Map<User, ReportViewModel.UserViewModel>(user);
// get the first unread message
var messageService = new MessageService();
var message = messageService.GetFirstUnread(user.Id);
model.Message = Mapper.Map<Message, ReportViewModel.MessageViewModel>(message);
// get the category navigation
var categoryService = new CategoryService();
var categoryNavigation = categoryService.GetCategoryNavigation(report.Id);
model.CategoryNavigation = Mapper.Map<IEnumerable<Category>, IEnumerable<ReportViewModel.CategoryNavigationViewModel>>(categoryNavigation);
return View(model);
}
It's fine to have multiple small services in your controller. However, there is one thing that's wrong here:
You services should be available through the entire controller and injected through the constructor to achieve loose-coupling.
So something like this:
private readonly IReportService _reportService;
private readonly IUserService _userService;
public SomeConstructor(IReportService reportService, IUserService userService, etc.)
{
_reportService = reportService;
_userService = userService;
// etc
}
That does look like a good approach, an alternative approach would to be split some of this up by using Child Actions - the best solution will depend upon your specific use case though.
If, for example, the ViewModel property CategoryNavigation was being used by the view to create a sort of navigation 'widget' that might be useful in several different Views, you might be better spliting this off into a ChildAction e.g.
[ChildActionOnly]
public ActionResult CategoryNavigationWidget(int reportId)
{
// get the category navigation
var categoryService = new CategoryService();
var categoryNavigation = categoryService.GetCategoryNavigation(report.Id);
return PartialView(categoryNavigation);
}
Any View could then render that ChildAction by going:
#{ Html.RenderAction("CategoryNavigationWidget", "Report",
new { reportId = Model.ReportId }); }
Whether or not this is a good idea will probably depend upon whether or not the 'widget' is reusable.

Asp.net MVC Model for view and Layout

I've been trying to find a good way to handle the Models of our Asp.net MVC websites when having common properties for all the pages. These properties are to be displayed in the Layout (Master Page). I'm using a "BaseModel" class that holds those properties and my Layout use this BaseModel as its model.
Every other model inherits from that BaseModel and each has specific properties relative to the view it represents. As you might have guessed, my Models are actually View Models even if that's not quite relevant here.
I have tried different ways to initialize the BaseModel values
By "hand" in every view
Having a base controller that has an Initialize virtual method to do it (so specific controller can implement specific common behavior for exemple)
Having a base controlelr that override OnActionExecuting to call the Initialize method
Using a helper class to do it outside of the controller
Using a Model Factory
But none of those really appeal to me:
Seems obvious to me, but DRY is one reason enough to justify that (actually I never tried that solution at all, I'm just putting it to be able to loop on that point in the last point).
I don't like that one because it means that whenever a new Controller is added, you need to know that it has to inherit from the BaseController and that you need to call the Initialize method, not to mention that if your controller has overriden the base one, to call the base anyway to maintain the values.
see next point
and 3. are a variation on the same topic but that doesn't really help with the issues of the second solution.
My favorite so far, but now I have to pass a few more variables to set those values. I like it for the inversion of dependence. But then if I want to provide values from the session, I need to pass them explicitly for exemple, then I'm back to square one as I have to provide them by hand (being references or through an interface of any kind)
Of course, (almost) all of those solutions work, but I'm looking for a better way to do it.
While typing this question, I found maybe a new path, the builder pattern that might also do, but implementations can become quickly a burden too, as we can have dozens of views and controllers.
I'll gladly take any serious recommandation/hint/advice/patterns/suggestion !
Update
Thanks to #EBarr I came up with another solution, using an ActionFilterAttribute (not production code, did it in 5 minutes):
public class ModelAttribute : ActionFilterAttribute
{
public Type ModelType { get; private set; }
public ModelAttribute(string typeName) : this(Type.GetType(typeName)) { }
public ModelAttribute(Type modelType)
{
if(modelType == null) { throw new ArgumentNullException("modelType"); }
ModelType = modelType;
if (!typeof(BaseModel).IsAssignableFrom(ModelType))
{
throw new ArgumentException("model type should inherit BaseModel");
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = ModelFactory.GetModel(ModelType);
var foo = filterContext.RequestContext.HttpContext.Session["foo"] as Foo;
model.Foo = foo;
model.Bar = somevalue;
filterContext.Controller.TempData["model"] = model;
}
}
Calling it is then really simple:
[Model(typeof(HomeModel))]
public ActionResult Index()
{
var homeModel = TempData["model"] as HomeModel;
// Add View Specific stuff
return View(homeModel);
}
And it gives me the best of every world. The only drawback is to find a proper way to passe the model back to the action.
Here it's done using the TempData object, but I also consider updating the model that one can find in the ActionParameters.
I'm still taking any serious recommandation/hint/advice/patterns/suggestion for that, or the previous points.
I went through almost exactly the same process as I dove into MVC. And you're right, none of the solutions feel that great.
In the end I used a series of base models. For various reasons I had a few different types of base models, but the logic should apply to a single base type. The majority of my view models then inherited from one of the bases. Then, depending on need/timing i fill the base portion of the model in ActionExecuting or OnActionExecuted.
A snippet of my code that should make the process clear:
if (filterContext.ActionParameters.ContainsKey("model")) {
var tempModel = (System.Object)filterContext.ActionParameters["model"];
if (typeof(BaseModel_SuperLight).IsAssignableFrom(tempModel.GetType())) {
//do stuff required by light weight model
}
if (typeof(BaseModel_RegularWeight).IsAssignableFrom(tempModel.GetType())) {
//do more costly stuff for regular weight model here
}
}
In the end my pattern didn't feel too satisfying. It was, however, practical, flexible and easy to implement varying levels of inheritance. I was also able to inject pre or post controller execution, which mattered a lot in my case. Hope this helps.
The idea that gave me #EBarr to use an action filter was actually working but felt wrong in the end, because there was no clean way to retrieve the model without passing through a viewbag, or the httpcontext items, or something alike. Also, it made mandatory to decorate every action with its model. It also made the postback more difficult to handle. I still believe that this solution has merits and might be useful in some specific scenarios.
So I was back to square one and started looking more into that topic. I came to the following. First the problem has two aspects
Initializing the data for the views
Rendering the data
While looking for more idea, I realized that I was not looking at the problem from the right perspective. I was looking at it from a "Controller" POV, whereas the final client for the model is the view. I was also reminded that the Layout/Master page is not a view and should not have a model associated with it. That insight put me on what feels the right path for me. Because it meant that every "dynamic" part of the Layout should be handled outside of it. Of course, sections seems the perfect fit for that, because of their flexibility.
On the test solution I made, I had (only) 4 different sections, some mandatory, some not. The problem with sections, is that you need to add them on every page, which can quickly be a pain to update/modify. To solve that, I tried this:
public interface IViewModel
{
KeyValuePair<string, PartialViewData>[] Sections { get; }
}
public class PartialViewData
{
public string PartialViewName { get; set; }
public object PartialViewModel { get; set; }
public ViewDataDictionary ViewData { get; set; }
}
For exemple, my model for the view is this:
public class HomeViewModel : IViewModel
{
public Article[] Articles { get; set; } // Article is just a dummy class
public string QuickContactMessage { get; set; } // just here to try things
public HomeViewModel() { Articles = new Article[0]; }
private Dictionary<string, PartialViewData> _Sections = new Dictionary<string, PartialViewData>();
public KeyValuePair<string, PartialViewData>[] Sections
{
get { return _Sections.ToArray(); }
set { _Sections = value.ToDictionary(item => item.Key, item => item.Value); }
}
}
This get initialized in the action:
public ActionResult Index()
{
var hvm = ModelFactory.Get<HomeViewModel>(); // Does not much, basicaly a new HomeViewModel();
hvm.Sections = LayoutHelper.GetCommonSections().ToArray(); // more on this just after
hvm.Articles = ArticlesProvider.GetArticles(); // ArticlesProvider could support DI
return View(hvm);
}
LayoutHelper is a property on the controller (which could be DI'ed if needed):
public class DefaultLayoutHelper
{
private Controller Controller;
public DefaultLayoutHelper(Controller controller) { Controller = controller; }
public Dictionary<string, PartialViewData> GetCommonSections(QuickContactModel quickContactModel = null)
{
var sections = new Dictionary<string, PartialViewData>();
// those calls were made in methods in the solution, I removed it to reduce the length of the answer
sections.Add("header",
Controller.UserLoggedIn() // simple extension that check if there is a user logged in
? new PartialViewData { PartialViewName = "HeaderLoggedIn", PartialViewModel = new HeaderLoggedInViewModel { Username = "Bishop" } }
: new PartialViewData { PartialViewName = "HeaderNotLoggedIn", PartialViewModel = new HeaderLoggedOutViewModel() });
sections.Add("quotes", new PartialViewData { PartialViewName = "Quotes" });
sections.Add("quickcontact", new PartialViewData { PartialViewName = "QuickContactForm", PartialViewModel = model ?? new QuickContactModel() });
return sections;
}
}
And in the views (.cshtml):
#section quotes { #{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "quotes").Value); } }
#section login { #{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "header").Value); } }
#section footer { #{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "footer").Value); } }
The actual solution has more code, I tried to simplify to just get the idea here. It's still a bit raw and need polishing/error handling, but with that I can define in my action, what the sections will be, what model they will use and so on. It can be easily tested and setting up DI should not be an issue.
I still have to duplicate the #section lines in every view, which seems a bit painful (especialy because we can't put the sections in a partial view).
I'm looking into the templated razor delegates to see if that could not replace the sections.

Where is the BEST PRACTICES place to put a list of Countries in a MVC Application?

In a heavy enviroment application, we have Users, Locations, bla bla bla... and we use in many situations a call to a service where we retrieve the list of countries.
Where is the 'best practice' or 'proper way' to implement this. This method is called in several places and many objects has a List<CountryVO> property.
Specially considering using Razor views an often having to add this property to ModelViews
The solution is using DAL / BLL / SERVICE / UI[s] architecture.
Real Example:
public class User {
...
...
public List<DeliveryZoneVO> DeliveryZones {get;set;}
public User() {
...
DeliveryZones = service.GetDeliveryZones().ToList();
}
}
The class DeliveryZoneVO comes from a webservice, so one property is
int IdCountry
The class User have a list of DeliveryZoneVO as presented on the class, the 'problem' here, is since it retrieves the data from a web service, I only have the ID of the country.
When I prepare the data in the controller to send to the View:
UserModelView userMV = new UserModelView();
userMV.user = service.GetUserById(1);
ViewData.Model = userMV;
BUT, inside userMV.user, I have DeliveryZones with a list of DeliveryZoneVO objects with IdCountries.
In the view, when I do (for example) :
#DisplayFor(m => m.user.DeliveryZones)
I want to show the Country Name, only have the ID. So i need a reference somewhere.. the question lies in where should that data needs to be placed that is considered BEST PRACTICES.
Is having in all modelview (in the case of the example, the UserModelView() the property Countries with a List ?
A good thing because this kind of issues is to have a BaseController class that derived from controller, and all the other controllers you have derived from it.
in the BaseController put a static List<CountryVO> property with getter only, this way it will be initialized once and will be accessible to all of your's controllers and views(If you pass it with the ViewModel or ViewBag).
Example:
public class BaseController : Controller
{
private static List<CountryVO> _allCountries;
public static List<CountryVO> AllCountries
{
get{ return _allCountries ?? _GetCountriesFromSomeWhere();}
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
ViewBag.AllCountries = this.AllCountries;
return View();
}
}
I would create a partial view that is responsible for just rendering the country list. Then any changes to how the list is rendered is can be made in just one place. I would create a model class that encapsulates calling the service to get the countries. Assuming that the country list is fairly static you could handle caching of the information in the model class for less calls to the service and better performance. Below is an example of a method in the model that gets the country list from the server cache if it is available.
const string cacheId = "deliveryZones";
public List<DeliveryZoneVO> GetDeliveryZones()
{
List<DeliveryZoneVO> deliveryZones = (List<DeliveryZoneVO>)HttpRuntime.Cache.Get(cacheId);
if (deliveryZones == null)
{
deliveryZones = service.GetDeliveryZones().ToList();
System.Web.HttpContext.Current.Cache.Insert(cacheId, deliveryZones);
}
return deliveryZones;
}

Resources