Why controllers run first in the ASP.NET MVC? - c#

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 *_^

Related

MVC - data calculations best practice - viewmodel vs. controller

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);
}
}

MVVM View Model Collections with Web API, View Model and Entity Framework

question about collection helper classes for view models with in a Web API <> View Models <> Entity Framework <> Database structure.
I'm trying to get a design started on a project which will use an "API" front end to get data, then HTML/Javascript pages to get and render.
In trying to create an MVVM approach to this I am not sure if what I want to implment is a good approch or not.
And that is a get Collection methods for the ViewModels. The user on the HTML side, will only be able to edit one Object at a time, so the collection is only to provide arrays/listing of the objects, making the Web API classes "less cluttered".
Using the Visual Studio MVC/Web API porject / c# (Visual Studio 2013 so MVC 5 components)
so using example of a Chair object
NOTE: in actual code would not do "new MyEntity().Chairs." all the time, it would be set as a variable with in the class. Simply written example long hand to make clear.
Web API part:
we will pluralize api objects front point
Web API controller = ChairsController
With 2 gets
namespace FurnitureAPI.Controllers
{
[Authorize]
public class ChairsController : ApiController
{
// GET api/chairs
public IHttpActionResult Get()
{
var chairs = ViewModels.Chairs.Get();
return Ok(chairs);
}
// GET api/chairs/5
//public string Get(int id)
public IHttpActionResult Get(int id)
{
var chair = ViewModels.Chairs.Get(id);
return Ok(chair);
}
public ... GET() {...} //all chairs
public ... GET(int id) {} //single chair where id
In the database side, we have Chair table. This will be singular for database.
Database already exists, so using Entity Framework and database first modelling we get in Entities
var furniture_db = new Models.FurnintureEntities();
and
Models.Chair
So in the middle want a View Model Chair.
namespace FurnitureAPI.ViewModels {
public class Chair {
public int ChairID { get; set; }
public string Name { get; set; }
public Chair() { }
public Chair(int chairid, string name) {
ChairID = chairid;
Name = name;
}
public Chair(Models.Chair db_chair) {
ChairID = db_chair.ChairID;
Name = db_chair.Name;
}
}
In my thinking, the Web API ChairsController I do not want to have any entity framework code.
but I want api to call something to get a List/Collection of Chair view models
So Im thinking, add Chairs as a class in the ViewModel namespace, which will be static.
so web api becomes
public IHttpActionResult Get()
{
var chairs = ViewModels.Chairs.Get();
return Ok(chairs);
}
and the View Model Collection Chairs then looks like
namespace FurnitureAPI.ViewModels {
public static class Chairs {
public static List<Chair> Get() {
List<Chair> chairs = (from s in new Models.FurnintureEntities().Chairs
select new ViewModel.Chair {
ChairID = s.ChairID,
Name = s.Name
}).ToList();
return chairs;
}
public static Chairs Get(int id) {
var chair = new FurnitureEntities().Chairs.SingleOrDefault(c => c.ChairID == id);
return chair != null ? new ViewModel.Chair(chair) : null;
}
}
So the question, is this Chairs collection wrapper okay/ideal/bad/run for the hills/other, or is there a different approach someone could point to?
I do not understand why struggling to find reference to on the internet, sure hope its not grumpy old age kicking in.
I find lots of WPF MVVM Entity Framework database first stuff, but I just want to make 'simple' Web API to expose the database to make HTML views with.
Maybe I do not need to bother with the View Model and simply serialize the Entity in the Web API methods?
Thanks.
Unfortunately I've been struggling with this too and it seems this subject doesn't get the love it should have.
Anyway, among the tools there are, you can count:
Breeze# (MIT)
TrackableEntitities (MIT)

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.

When should I write code in the controller vs. model?

Without a doubt I know what the controllers and models are used for. However, I am able to write code that interacts with my db, for example adding users to a table, on either the controller or model. At what times should I write code in the controller vs. in model? Even though both work, what would be a more organized or practical way. Could you please post examples if the answer is ambiguous?Thx
For that, you should add a logic layer or logic classes. The controller should determine wants to do and can do, shuffle them in the right direction (logic layer), then determine what to show the user after the logic. Putting the logic in a separate layer will help keep your controllers lean and promote code reuse.
In the domain core, we only have models with properties. All logic is performed in a different layer, except for things like a property that returns fields concatenated in a format.
Code to access the database should be in service layer instead of keeping in Controller or Model.
Accessing Database Entities from Controller
Here is my answer for the above question, you can also read others answers why you should keep in separate layer.
namespace MyProject.Web.Controllers
{
public class MyController : Controller
{
private readonly IKittenService _kittenService ;
public MyController(IKittenService kittenService)
{
_kittenService = kittenService;
}
public ActionResult Kittens()
{
// var result = _kittenService.GetLatestKittens(10);
// Return something.
}
}
}
namespace MyProject.Domain.Kittens
{
public class Kitten
{
public string Name {get; set; }
public string Url {get; set; }
}
}
namespace MyProject.Services.KittenService
{
public interface IKittenService
{
IEnumerable<Kitten> GetLatestKittens(int fluffinessIndex=10);
}
}
namespace MyProject.Services.KittenService
{
public class KittenService : IKittenService
{
public IEnumerable<Kitten> GetLatestKittens(int fluffinessIndex=10)
{
using(var db = new KittenEntities())
{
return db.Kittens // this explicit query is here
.Where(kitten=>kitten.fluffiness > 10)
.Select(kitten=>new {
Name=kitten.name,
Url=kitten.imageUrl
}).Take(10);
}
}
}
}
ASP.NET MVC and MVC, in general, is a presentation layer pattern; thus your interaction with the database should be in a layer beyond the presentation layer, usually a data-access layer, but it could be a service layer or business layer as well.

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