MVC - data calculations best practice - viewmodel vs. controller - c#

rI need some advice on where to run a calculation on data.
I have a viewmodel that contains all the fields that I need for my calculation and I created the following for one of my calculations:
public class CommissionVM
{
public int? LoanAmountLock { get; set; } // from loan table
public decimal BranchRev { get; set; } // from revenue table
public decimal BranchProcessFee { get; set; } // from revenue table
public decimal BranchGrossTotal
{
get
{
return Convert.ToDecimal(LoanAmountLock * (BranchRev/ 100) + BranchProcessFee);
}
}
}
I tried to use the Model.BranchGrossTotal in my view, but it is returning 0. I think I have an order-of-operations problem. The values LoanAmountLock, BranchRev, and BranchProcessFee are returned as the results of a query:
public ActionResult Reconcile(int? id, string RevenueLoanType)
{
var model = new CommissionVM()
{
Loan = db.Loan.FirstOrDefault(a => a.id == id ),
BranchRevenues = db.BranchRevenues.FirstOrDefault(a => a.RevenueLoanType == RevenueLoanType),
};
return View(model);
}
I originally was able to get these calculations to work by doing all the math in the controller after I populate the viewmodel with the query, but there will be about 10 calculations, and from what I understand, I shouldn't clutter up my controller with business logic.
What is the best solution for this? Do I need to create another class for the calculations? If so, how do I populate that class with my data and use it in my controller?
EDIT: I am not sure how to set up the business classes and use them in the controller. Can anyone point me in the direction of a tutorial?

You should not do the calculation in your controller nor in your view model. You should do it in the Business Layer. Think about View Models are really simple classes that contain data to be displayed to the user, that's it.
Regarding the calculation, you should convert one of the terms to decimal, not the result of the calculation. If you divide integers, you get an integer.
You could create a class and call it CommissionService for example. That class should call your Data Layer to get the data, do any extra calculation and return the data (maybe a DTO) to the controller. The controller should create View Models based on the DTO and send them to the view.
Read these articles:
1) https://msdn.microsoft.com/en-us/library/hh404093.aspx
2) http://www.asp.net/mvc/overview/older-versions-1/models-%28data%29/validating-with-a-service-layer-cs
3) http://blog.diatomenterprises.com/asp-net-mvc-business-logic-as-a-separate-layer/
4) http://sampathloku.blogspot.com.ar/2012/10/how-to-use-viewmodel-with-aspnet-mvc.html

I don't like calculations on my view models -- you can't reuse the calculation easily elsewhere and it is harder to test and debug. Create separate classes to do business logic.
Your business logic classes can either return your view models or return values you use to populate those models. The trade-off is ease of use with reusability.
I generally favor returning the value rather than a big object so my services are more reusable.
Controller
public class BranchController : Controller
{
private IBusinessService service;
public BranchController()
{
this.service = new BusinessService(db);
}
public ActionResult Reconcile(int? id, string RevenueLoanType)
{
var model = new CommissionVM
{
BranchGrossTotal = this.service.GetBranchGrossTotal(id, RevenueLoanType),
...
};
return View(model);
}
}
Service
You can make any number of these and your controllers would use them as needed. If you need a query you should pass the DbContext instance or you may have problems with related entities on separate contexts.
public interface IBusinessService
{
decimal GetBranchGrossTotal(int id, string revenueLoanType);
}
public class BusinessService : IBusinessService
{
private DbContext db;
public BusinessService(DbContext db)
{
this.db = db;
}
public decimal GetBranchGrossTotal(int id, string revenueLoanType)
{
var branch = db.Branch.First(b => b.Id == id);
// do stuff
return total;
}
}
You could fully-populate and return a view model in your GetBranchGrossTotal() if you choose.

First of all, the properties you are assigning to your CommissionVM on your controller do not match the ones declared on your model. You assign Loan and BranchRevenues, when you have only LoanAmountLock and BranchRevs available on your model.
Please notice that the Loan property is an object itself, and the LoanAmountLock must be retrieved from this object (Loan.LoanAmountLock). The same happens with the BranchRevenues object. You should assign the BranchRevs to the respective property of the BranchRevenues object as needed. If you do not do this, then the values will default to 0 and when trying to calculate the BranchGrossTotal it will obviously be 0.
Another reason, assuming that you are correctly populating your model properties, is that the FirstOrDefault method, renders null values because there is no such entity. This will result also in the BranchGrossTotal to be 0.
You are right that you do not need to clutter your controller neither with calculations nor with db access. I would create a business class ComissionBusiness and instantiate it at the top of your controller. This class would have a method which performs all calculations. You should move the Reconcile method to your new business class method and call it on the reconcile action. Something like (excuse the lack of syntax)
public MyController : Controller {
public ComissionBusiness comissionBusiness;
public MyController(){
comissionBusiness = new ComissionBusiness();
}
public ActionResult Reconcile(int? id, string RevenueLoanType)
{
var model = comissionBusiness.Reconcile(id, revenueLoanType);
return View(model);
}
}

Related

Who populates the ViewModel in ASP MVC 5

Whose responsibility is it to populate the values in an ASP MVC 5 architecture (C#, EF), for e.g. if we have PurchaseRecordsViewModel , PurchaseRecords Domain Model , PurchaseController
Does the code to populate data (time, cost etc) go it the viewmodel, right in its very own the viewmodel go in the PurchaseRecordsViewModel ?
Or, does the code go in the Action method of the PurchaseController
View models are typically just dumb collections of properties. Populating a view model typically rests inside of your service layer or, if you don't have one, your action method.
Think of the roles this way.
A domain model is a direct mapping to a database table.
A view model is a collection of properties needed to display a view.
A service layer gets/uses one or more domain models and populates a view model.
A service layer also can take a view model and create/update one or more domain models
A controller action method is the glue between the two. It calls a service layer to get (GET) a view model and passes it to a view. These action methods also take (POST) a view model and pass it to the service layer to do whatever needs to be done to it.
Another question typically asked is why can't I use domain models for a view? You can, but typically you run into things like, needing data from more than one domain model, not needing all the properties that are in the domain model and lastly, you now would have to worry about properties being updated on the domain model that you did not intend.
Expanding upon Tommy's answer, here is some code to go along with his description.
//Controller
public ActionResult Index()
{
List<OrderViewModel>() model = new List<OrderViewModel>();
model = new ServiceClass().GetOrders();
return View(model);
}
//here is your Service Class, this layer transfers the Domain Model into your ViewModel
public List<OrderViewModel> GetOrders()
{
List<OrderDomain> model = new List<OrderDomain>();
model = new DataAccess().GetOrders();
List<OrderViewModel> viewModel = new List<OrderViewModel>();
foreach (var order in model)
{
OrderViewModel vm = new OrderViewModel();
vm.OrderId = order.OrderId;
vm.OrderName = order.OrderName;
viewModel.Add(vm);
}
return viewModel;
}
//some DataAccess class, this class is used for database access
Public List<OrderDomain> GetOrders()
{
List<OrderDomain> model = new List<OrderDomain>();
using (var context = new MyEntities())
{
model = (from x in context.Order
select new OrderDomain
{
OrderId = x.OrderId,
OrderName = x.OrderName
}).ToList();
}
return model;
}
Edit:
This seems to be a mildly popular answer so I would like to mention I no longer follow this pattern. Instead I've been using mediatr and vertical slice architecture.
Ideally, PurchaseRecordViewModel should populate itself by getting PurchaseRecordsDomainModel. It should contain simple mapping of properties, and possibly some formatting of the output you're going to use in your view.
PurchaseRecordsViewModel
public class PurchaseRecordsViewModel
{
public IEnumerable<PurchaseRecordViewModel> PurchaseRecords {get;set;}
}
PurchaseRecordViewModel
public class PurchaseRecordViewModel
{
public DateTime Date {get;set;}
public decimal Cost {get;set;}
// .... some other properties
public PurchaseRecordsViewModel(PurchaseRecordsDomainModel domainModel)
{
Date = domainModel.Date;
Cost = domainModel.Cost;
// .... some other property mappings
}
}
What your action method on PurchaseController should do, is orchestrating the process of getting your PurchaseRecordsDomainModel, creation of PurchaseRecordsViewModel from PurchaseRecordsDomainModel and passing it to the View. Action method itself shouldn't contain any code that deals with connecting and retrieving data from database (in your case querying EF context), or any business logic. You should try to have loosely coupled modules, talking to each other via abstractions, this way you will ensure your application is maintainable, extensible and testable.
Also, try to draw clear separation between various layers of your system. For example, it is not a good idea to have EF entities as Domain Model Entites. You don't want your business logic layer to depend on data access layer, think of it this way, what if at some point of time in the future, you are moving away from EF and using some other ORM or even other technology to store and query data. You don't want to change business logic layer just because you're changing your data access layer. So to go from words to code in your case.
Considering that you already have your view and view model, I would create PurchaseRecordsService class in domain layer(please note depending in your case you might not use Repositories, but some other technique, this example is mainly to illustrate my point)
public class PurchaseRecordsService
{
private readonly IPurchaseRecordsRepository _purchaseRecordsRepository;
public PurchaseRecordsService(IPurchaseRecordsRepository purchaseRecordsRepository)
{
if(purchaseRecordsRepository == null)
{
throw new ArgumentNullException("purchaseRecordsRepository");
}
_purchaseRecordsRepository = purchaseRecordsRepository;
}
public IEnumerable<PurchaseRecordsDomainModel> GetPurchaseRecords()
{
// trivial case, real code can be more complex
return _purchaseRecordsRepository.GetPurchaseRecords();
}
}
Then in your domain layer, you could define IPurchaseRecordsRepository
public interface IPurchaseRecordsRepository
{
IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords();
}
The idea is, our PurchaseRecordsService needs a way to communicate with databases, so whoever uses it, must supply implementation of IPurchaseRecordsRepository. Next step is to move to our data access layer and create implementation class of IPurchaseRecordsRepository.
public class EfPurchaseRecordsRepository: IPurchaseRecordsRepository
{
private readonly EfObjectContext _objectContext;
public EfPurchaseRecordsRepository(string connectionString)
{
_objectContext = new EfObjectContext(connectionString);
}
public IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords()
{
var purchaseRecords = (from p in _objectContext.PurchaseRecords
....
select p).AsEnumerable();
return purchaseRecords .Select(p => p.ConvertToDomainPurchaseRecord());
}
}
And the last piece - we need to define our Action in PurchaseController
public class PurchaseController: Controller
{
private readonly IPurchaseRecordsRepository _repository;
public PurchaseController(IPurchaseRecordsRepository repository)
{
if(repository == null)
{
throw new ArgumentNullException("repository");
}
_repository = repository;
}
public ActionResult Index()
{
var purchaseRecordsService = new PurchaseRecordsService(_repository);
var purchaseRecordsViewModel = new PurchaseRecordsViewModel();
var purchaseRecords = purchaseRecordsService.GetPurchaseRecords();
foreach(var purchaseRecord in purchaseRecords)
{
var purchaseRecordViewModel = new PurchaseRecordViewModel(purchaseRecord);
purchaseRecordsViewModel.PurchaseRecords.Add(purchaseRecordViewModel);
}
return View(purchaseRecordsViewModel);
}
}
To recap, what we have is loosely coupled code, our Presentation and Data Access Layers don't know about each other, and they depend only on Domain layer. If you need, you can replace MVC front end with WPF for example, move from EF to another technology, your code is testable.
Ideally, your view model should be unaware of your domain model, so I'd say that you put your population logic in your controller, perhaps packed away in some sort of mapping/population utility class.
But remember, when it comes to questions about where to put certain logic, personal preference goes a long way.

.Net MVC binding dynamic Type to a Model at runtime

I have a slightly long conceptual question I'm wondering if somebody could help me out with.
In MVC I've built a website which builds grids using kendoui's framework.
All the grids on my website are constructed exactly the same except for the model they use and the CRUD methods that need to be implemented for each model. I set things up where each Model implement an interface for CRUD methods like below to get the logic all in one place.
//Actual interface has variables getting passed
public interface IKendoModelInterface
{
void Save();
void Read();
void Delete();
}
public class Model1: IKendoModelInterface
{
[Key]
public int IdProperty1 { get; set; }
public int SomeProperty2 { get; set; }
public string SomeProperty3 { get; set; }
public void Save(){
//Implement Save
}
public void Read(){
//Implement Read
}
public void Delete(){
//Implement Delete
}
}
Then to speed up the writing of all the scaffolding Action methods needed to get the grids to work I created an abstract Controller that can call the interface methods of the Model that gets passed into it.
//Implement the AJAX methods called by the grid
public abstract class KendoGridImplController<T> : Controller where T : class, IKendoModelInterface
{
// Method called from kendo grid
public virtual ActionResult Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<T> createdRecords)
{
//Invoke Create Method for Model and return results
}
public virtual ActionResult Read([DataSourceRequest]DataSourceRequest request, int Id)
{
//Invoke read method for model and return results
}
//Update and Delete also implemented..
}
Then I just need a Controller per model that implements the abstract controller above passing in the type of Model being used.
public class ResponsibilityMatrixController : KendoGridImplController<Model1>
{
//Set up the page the grid will be on
public ActionResult Index(int id)
{
return View("SharedGridView", id);
}
//Can override abstract methods if needed but usually won't need to
}
I'm wondering if I can take this one step further or if I've reached the end of the road. To me it just seems like more repeated code if I have to create a controller per Model that does nothing but pass in the type to the abstract controller and calls the same View.
I attempted for quite a while yesterday to figure out if I could dynamically assign the type to the abstract controller. I setup something where I was sending back the type of model via strings and I could still invoke the methods needed. Where it failed, was that the mapping could no longer be done on any of the controller actions by default since the type isn't known at compile time. eg
public virtual ActionResult Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<T> createdRecords)
createdRecords can't be bound like this if T that's passed in is an interface and not the Model itself and I've found no real way to map the form data to an instance of a type that isn't known at compile time.
I'm wondering if there's an easy way to do this mapping between an instance of the type of object getting passed in that I can figure out at runtime, if there's some other way to set this up that I'm overlooking or if both those things are going to be way too much work and I should just not attempt something like this and build a controller per model like I do now?
In case anybody else finds this in the future here's what I've done so far to solve my issue. First I downloaded the impromptu-interface code lib which is incredibly helpful when dealing with dynamic types.
Then for the abstract controller's save methods where it was important that I could bind back to the original object type I did this.
// Method called from kendo grid
public virtual ActionResult Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<ExpandoObject> createdRecords)
{
Type originalGridType = GetTypeOfModelUsingCustomCodeIDevelopedEarlier();
foreach (ExpandoObject record in createdRecords)
{
var convertedType = Impromptu.InvokeConvert(record, originalGridType);
T objectInstance = Impromptu.ActLike(convertedType);
objectInstance.Save();
}
}
Then I just needed to add a cast in my model that could convert from the ExpandoObject to my model. An extra method that I still wish didn't have to be there but with some helper methods that I wrote it's not a lot more code to make happen.
public static implicit operator Model1(ExpandoObject expando)
{
Model1 model = new Model1();
//Set fields of model...
//....
return model;
}
From here everything works front to back. Maybe there's a better way but this is the best I could come up with so far.

Injecting dependency into DTO

Introduction
I'm using ASP.Net MVC3. My Controllers talk to a service layer, and the service layer talks to a Data Acces layer which uses Entity Framework.
I get a specific entity using Entity Framework. This entity is converted into a DTO. Then I deliver this DTO to a MVC controller. Something like this:
pseudo code:
// This is inside my Service Layer
var entity = DataAccess.GetById(id);
var dto = createDtoWithValuesFrom(entity);
return dto; // Return dto to MVC controller
In this DTO I would like to use a dependency, to for example a Calculator. Let's say my DTO looks like this:
public class Customer
{
private ICalculator Calculator;
public class Customer(ICalculator calculator)
{
Calculator = calculator;
}
public string Name { get; set; }
public decimal Discount
{
get
{
return Calculator.Discount();
}
}
}
Problem
How do I instanciate my DTO, and let Autofac inject a calculator?
I can think of a way to do this:
var calculator = DependencyResolver.Current.GetService<ICalculator>;
var dto = new DTO(calculator );
But I don't know if this is the best way to do it, since it smells of ServiceLocator, and I've read that it's not prefered to use that.
DTOs normally have some properties and do not contain any logic.
You should consider a design where your MVC-Controller does something like this:
Get the customer from the service/dataaccess
Calculate the discount by invoking the ICalculator which could be passed to the Controller using constructor injection (or call an extra service which does the calculation)
Create a new model class which contains the customer and the calculated discount and pass this model to the view.
public class Model
{
public Customer Customer { get; set; }
public double Discount { get; set; }
}
public class SomeController : Controller
{
private readonly DataAccess dataAccess;
private readonly ICalculator calculator;
public SomeController(DataAccess dataAccess, ICalculator calculator)
{
this.dataAccess = dataAccess;
this.calculator = calculator;
}
public ActionResult Index(int id)
{
var model = new Model();
model.Customer = this.dataAccess.Get(id);
model.Discount = this.calculator.Calculate(customer);
return View(model);
}
}
First of all: A DTO is not a View Model.
A DTO (Data Transfer Object) is a dummy class (it's really not a first class (OOP) citizen). It's purpose is only to flatten hierarchies and transport information between different layers/tiers.
A View Model is used to adapt a model (as in MVC) so that it fits a view better. It's purpose is simply to remove logic from the view and hide details regarding the Model that should not be used/available in the View.
Neither a DTO or a ViewModel should be used for anything else unless you want to violate Single Responsibility Principle.
What you are asking for should be done in your Controller (since it's the glue between the Model and the View). Hence add the ICalculator to the constructor of your controller.

Why controllers run first in the ASP.NET MVC?

I want to improve current implementation of the ASP.NET MVC Framework.
Current code:
routes.MapRoute(null, "I-want-to-fly", new { controller = "Airport", action = "Fly" });
public class AirportModel
{
public List<Plane> Planes { get; private set; }
public List<Pilot> Pilots { get; private set; }
public void AddFly(Plane plane, Pilot pilot, Passenger passenger)
{
// . . .
}
}
public class AirportController
{
private AirportModel model;
[HttpGet]
public ViewResult Fly(string from, string to)
{
var planes = return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
return View(planes);
}
[HttpPost]
public ActionResult Fly(Plane plane, Passenger passenger, DateTime time)
{
if (!(ModelState.IsValid && plane.TimeOfDeparture == time))
return View();
var pilot = (from p in model.Pilots
where p.Free && p.CanAviate(plane.Id)
select p).First();
model.AddFly(plane, pilot, passenger);
return RedirectToAction("Succeed");
}
}
My proposal:
routes.MapRoute(null, "I-want-to-fly", new { model = "Airport", action = "Fly" });
public class AirportModel
{
private List<Plane> planes;
private List<Pilot> pilots;
private void AddFly(Plane plane, Pilot pilot, Passenger passenger)
{
// . . .
}
[HttpGet]
public ViewResult Fly(string from, string to)
{
var planes = return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
return View(suitablePlanes);
}
[HttpPost]
public ActionResult Fly(Plane plane, Passenger passenger, DateTime time)
{
if (!(ModelState.IsValid && new PlaneController().CanFly(plane, time)))
return View();
var pilot = (from p in pilots
where p.Free && p.CanAviate(plane.Id)
select p).First();
AddFly(plane, pilot, passenger);
return RedirectToAction("Succeed");
}
}
public static class PlaneController
{
public static bool CanFly(Plane plane, DateTime time)
{
return plane.TimeOfDeparture == time; // it will be more complex
}
}
You see, in such way we don't need excessive count of controllers and their methods. Model would create controller only by perforce: mostly to verify user input (not input validation, business validation).
What do you think, can this idea have a continuation? Or, what is wrong with it?
Thanks for your replies!
UPDATE: I noticed, that we need to replace implementations of controller and view as a result of changing the model's state (mostly). So, if model causes to change the implementation, why model cannot do it?
UPDATE 2: It seems to me I explained incorrectly. I don't want model to do all work, of course no! I try to say, that not controller should decide what to do with model and what view is the most suitable for this user request.
Doesn't it strange, that model doesn't know how to visualize itself, but some controller knows?
Doesn't it strange, than we need controller for the GET request, where there is nothing to control?
I try to remove those strangenesses.
UPDATE 3: I understand that it cannot be applied anywhere. The main question is: can it improve some part of current implementations of MVC ? Mostly I'm interested in ASP.NET MVC -- can we
remove redundant controllers or some its methods
work directly with models
using this idea? Is it possible and what are the problems of this idea?
Found problems:
More strong connection between model and view/controller -- but currently I don't think it's a problem. Actually it shows that views and controllers were created in help for the major element -- model.
UPDATE 4: I changed the code, showing "before/after". Maybe this example will be better.
Doesn't this just violate the whole idea of MVC? Your model is separated from your controller and your view. In this way (the way you propose) you would not be able to replace your model by another implementation, or your controller for that matter.
updated I:
You could of course let your model do the part of the controller as well, but from that moment on you're not talking about the MVC design pattern anymore. For MVC, the model does and should not now about the view. That's the controllers job.
The controller receives user input and initiates a response by making calls on model objects. A controller accepts input from the user and instructs the model and viewport to perform actions based on that input.
In the MVC pattern, the Model isn't just fixed to your database model, it could be a combination of your database model and a repository pattern as well, where you implement your business logic.
The biggest problem I see with your proposal is that it makes code non-reusable. I get a model that is tightly coupled with it's views which I really don't want if I want to reuse the model in whatever way I might want to.
Update II
I think your are being mislead by the actual word Controller, I had that thought for a while and your latest comment sort of confirms this for me
Controllers are some objects, that check correspondence of user input to business-logic.
Controllers act upon user input, they might check the user input but their responsibility for checking validity stops there. Business logic goes in the Model (again, the Model as defined by the MVC pattern, not the model as in datamodel). Their main purpose is deciding what View to display.
Also from one of your latest comments:
How do you think, if [asp.net mvc] would be developed in my way, would it solve problem of redundant controllers?
Asp.Net MVC follows the MVC design pattern. Your proposal does not. It seem more like a ModelControlled View pattern, just to coin a name. Also, there are no redundant controllers, the controllers are no problem, they are an integral part of the solution.
And an effort to simplistically clarify what I mean with a code example:
namespace DataProject.Model
{
public class AirportModel
{
public List<Plane> Planes { get; set; }
public List<Pilot> Pilots { get; set; }
public List<Passenger> Passengers { get; set; }
public List<Flight> Flights { get; set; }
}
}
namespace SomeProject.Repository
{
public class AirportRepository
{
private DataProject.Model.AirportModel model;
//constructor sets the model somehow
public bool AddFlight(Plane plane, List<Passenger> passengers, DateTime time)
{
//Business logic
if (plane.TimeOfDeparture != time) return false;
var pilot = (from p in model.Pilots
where p.Free &&
p.CanAviate(plane.Id)
select p).FirstOrDefault();
//More Business logic
if (pilot == null) return false;
//Add plane, pilot and passenger to database
model.Flights.add(new Flight{Pilot = pilot, Plane = plane, Passengers = passengers});
//Even here you could decide to do some error handling, since you could get errors from database restrictions
model.Save();
return true;
}
public List<Planes> GetPlanes(string from, string to)
{
return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
}
}
}
namespace MVCApp.Controllers
{
public class AirportController
{
private SomeProject.Repository.AirportRepository repository;
[HttpGet]
public ViewResult Fly(string from, string to)
{
var viewModel = repository.GetPlanes(from, to);
return View(viewModel);
}
[HttpPost]
public ActionResult Fly(Plane plane, List<Passenger> passengers, DateTime time)
{
if (!ModelState.IsValid) return View();
if (!repository.AddFlight(plane, pilot, passenger)) return View();
return RedirectToAction("Succeed");
}
}
}
No offense intended but how exactly is this an improvement?
So you've made a class called a PersonModel that isn't really doing "model things" at all - it is doing the work that Controllers do - you've got it handling gets and posts and calling out for the display of Views and then you've got a static "Controller" that really controlling nothing and is concerning itself with business logic. Honestly, I don't get how this is an improvement.
A concrete example of is you've got a controller checking whether Age >= 18, which is a very "business rules" thing for a controller to be doing. That's not the purpose of a controller. That's the job of a model object - to concern itself with things like business logic. Controllers, as one person put it, are more of an electronic curator. In your example, you've relegated it to something far less than a curator.
There are distinct roles that objects play in an MVC application. Views show us stuff and provide us with ways to interact with the application. Controllers handle the input coming from the View and serve up views that are needed. Models provides a place to put data and the logic and business rules that the model encompasses. Services handle things like persisting data to some store, like a DB.
You can do everything in one class without controllers at all but you want "separte of concerns"
So the controller is responsible for the http request and validation and the model is responsible only for the data.
You are the programmer then you could agree or disagree with MVC pattern.
But the pattern which you described doesn't support the separation of concern and it breaks out the whole idea about MVC
This has nothing to do with MVC.
This is 'MVNothing'
:)
Just kidding *_^

How to avoid placing domain logic in controller?

On PRO ASP.NET MVC book:
It is certainly possible to put domain
logic into a controller, even though
you shouldn’t, just because it seems
expedient at some pressured moment.
Just a contrived example, if the application doesn't allow negative order, where to put the changing of quantity to 1? If we follow the principle that domain logic shouldn't be placed in controller, this is certainly not advisable to use:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult PlaceOrder(Order order)
{
if (ModelState.IsValid)
{
order.Submit();
return View("Thanks", order);
}
else
{
if (order.Quantity <= 0)
{
ModelState.Remove("Quantity");
order.Quantity = 1;
}
return View(order);
}
}
So the following code is the right code that adheres to MVC principle, i.e. it follows separation of concerns, if it's domain logic you should not see its code in controllers. So this is how I tried placing the domain logic in Model:
public class Order : IDataErrorInfo
{
public int OrderId { set; get; }
public int ProductId { set; get; }
public int Quantity { set; get; }
public string Error { get { return null; } }
public string this[string propName]
{
get
{
if (propName == "Quantity" && Quantity <= 0)
{
Quantity = 1;
return "0 or negative quantity not allowed, changed it to 1";
}
else
return null;
}
}
}
Controller (sans the domain logic):
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult PlaceOrder(Order order)
{
if (ModelState.IsValid)
{
order.Submit();
return View("Thanks", order);
}
else
{
// Response.Write(order.Quantity.ToString()); // this was changed in Model
return View(order); // but the View didn't reflect that fact
}
}
The only problem with that approach, the Model(Order) can't influence the ModelState, and such, the program always display whatever is last entered by the user.
What's the best approach so I can still avoid placing domain logic in controller and the View is still able to reflect the values of Model's properties?
Validation is not controllers task. You can Put all required logic in different module, and just propogate requests there.
Ah, but the business layer can influence model state. Check out this tutorial on validating with a service layer. It's also a great intro to repository and Inversion of Control.
The general approach is to create a wrapper for the model state that implements a simple interface for adding errors to the model state. Your business layer acts against the interface - thus it has no ties to your modelstate. Your unit tests can implement a fake wrapper that also implements that same interface.
It looks like your specific example is changing the user's invalid input to valid input. My suggestion would be to simply leave the invalid input and use AddModelError to reflect that when the controller returns the view.

Categories