Reputation: 7417
I am using MVC3 and have implemented IoC to provide service/manager objects to controllers through constructor arguments. These in turn, may get passed to models.
The problem I am having is that passing these objects all over the place can get cumbersome.
Example:
public CartController(
ICartManager cartManager,
IProductManager productManager,
IUpsellManager upsellManager,
IAccountManager accountManager,
... more ...)
{
... store to class variables ...
}
public ActionResult Index()
{
...
CartModel model = new CartModel(
cartManager,
accountManager,
upsellManager,
productManager,
... );
return View(model);
}
And the cart model may have sub models to which it must pass parameters. As you see, it all gets very cumbersome. I've ready if you have so many constructor arguments, your controller may be doing too much, but this is a complex page, and the site will contain many other complex pages. I don't want to have to pass so many things, but how can I not pass them and maintain a control?
I'm tempted to use the DependencyResolver in the models but that defeats the purpose and Service Locator is known anti-pattern.
How can I avoid passing so many arguments without giving up the benefits of IoC?
Upvotes: 1
Views: 221
Reputation: 93434
You shouldn't be passing all those services to your view. What that means is that your template now has to perform actions to call this data, which complicates things (as you've seen). In addition to needless complexity, it also creates a dependency of these services in the view, and tighly couples the view to these services. If you change the services, you then have to change all the views that use it.
First thing you need to do is create a ViewModel that contains all the Data the view will need. Then, you need to find a way to map your manager classes returned data into this view. You can do it manually in the controller, or use something like AutoMapper to do the translations.
Another option is to either refactor ICartManager so that it returns all the data (your CartManager class would then have constructor injection of the other services) or create a different service that aggregates them and constructs an object that you can map to your View model.
You should never have to pass those methods around. They should always be injected into an object.
Upvotes: 1
Reputation: 102753
I like using a factory pattern for this scenario, where you need to instantiate class B (CartModel) within class A (CartController), and there are a bunch of dependencies in B that A doesn't need.
public CartController(
ICartModelFactory factory
... more ...)
{
... store to class variables ...
}
public ActionResult Index()
{
...
CartModel model = factory.GetInstance(
... );
return View(model);
}
Upvotes: 1
Reputation: 2439
The advantage of using dependency inversion is to enforce clear separation of responsibilities between the various roles in the application. The Controller it is really just interested in dealing with HTTP Request and the HTTP Response. All other logic is dealt with elsewhere and the controller delegates to types it is dependent upon to do this work. This pattern is then repeated for each function in the system. One way to detect that this pattern isn't being followed is to use the new
operator to create a type and pass in dependencies to the constructor. This is the job for the IoC container you are using.
I would suggest swapping the logic around and having the CartManager return a CartModel for the view. This could be some kind of DTO which is specific to the view. How the data gets put in to this object is the responsibility of the CartManager. If it needs the use of other services they can be injected in to it's constructor.
public CartController : Controller
private ICartManager _cartManager;
public CartController(ICartManager cartManager) {
_cartManager = cartManager;
}
[HttpGet]
public ActionResult Index(int userId) {
var model = _cartManager.CreateCart(int userId);
return View(model);
}
...
public class CartManager : ICartManager {
IDbService _dbService;
public CartManager(IDbService dbService){
_dbService = dbSerivce;
}
CartModel CreateCart(int userId) {
var user = _dbService.FindTheUser(int user);
var cartModel = new CartModel { userId = userId, Name = user.Name };
/* other stuff to map up a cartmodel
return cartModel;
}
}
This idea is not just specific to the use of IoC containers but is also good practice for creating MVC applications. I also found this post by Rob Ashton to be a good guide.
Upvotes: 3