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" &&开发者_Go百科 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.
精彩评论