Your question returns to the principle of ownership:
Anyone who has the right to own a resource must dispose of it.
Although ownership may be transferred, you should not usually do so. In your case, the ownership of IDbConnection transferred from ordersService to OrdersRepository (since OrdersRepository uses a connection). But in many cases, the OrdersRepository cannot know whether a connection can be established. It can be reused in the graph of objects. Therefore, in the general case, you should not place objects passed to you through the constructor.
Another thing is that the consumer of dependencies often cannot know whether the dependency needs, because regardless of whether the dependency should be selected, this is an implementation detail. This information may not be available in the interface.
So reformat the OrdersRepository as follows:
public class OrdersRepository : IOrdersRepository { private IDbConnection _db; public OrdersRepository(IDbConnection db) { _db = db; } }
Since the OrdersRepository does not receive ownership, IDbConnection does not need to host IDbConnection , and you do not need to implement IDisposable . This explicitly takes responsibility for removing the connection to ordersService . However, ordersService itself does not need an IDbConnection as a dependency; it just depends on the IOrdersRepository . So, why not move the responsibility for plotting the object from ordersService :
public class OrdersService : IDisposable { private readonly IOrdersRepository _orders; public ordersService(IOrdersRepository ordersRepo) { _orders = ordersRepo; } }
Since ordersService has nothing to do with itself, there is no need to implement IDisposable . And since it now has only one constructor that accepts the required dependencies, the class has become much easier to maintain.
Thus, this transfers responsibility for creating the object graph in the OrdersController . But we have to apply the same template to the OrdersController :
public class OrdersController : Controller { private ordersService _orderService; public OrdersController(ordersService o) { _orderService = o; } }
Again, this class has become much easier to understand, and it does not need to dispose of anything, since it does not have or did not own any resource.
Of course, we simply postponed and postponed our problems, since, obviously, we still need to create our OrdersController . But the difference is that we have now shifted the responsibility for plotting objects to one place in the application. We call this place the root of the composition .
Dependency frameworks can help you make your root composition supported, but even without a DI framework, you can create your object graph fairly easily in MVC by implementing a custom ControllerFactory :
public class CompositionRoot : DefaultControllerFactory { protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType) { if (controllerType == typeof(OrdersController)) { var connection = new Ajx.Dal.DapperConnection().getConnection(); return new OrdersController( new OrdersService( new OrdersRepository( Disposable(connection)))); } else if (...) {
You can connect your custom factory controller in the global asax of your MVC application as follows:
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { ControllerBuilder.Current.SetControllerFactory( new CompositionRoot()); } protected void Application_EndRequest(object sender, EventArgs e) { CompositionRoot.CleanUpRequest(); } }
Of course, all this becomes much easier if you use the Injection Dependency framework. For example, when you use Simple Injector (I am the lead developer for Simple Injector), you can replace all of this with the following lines of code:
using SimpleInjector; using SimpleInjector.Integration.Web; using SimpleInjector.Integration.Web.Mvc; public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { var container = new Container(); container.RegisterPerWebRequest<IDbConnection>(() => new Ajx.Dal.DapperConnection().getConnection()); container.Register<IOrdersRepository, OrdersRepository>(); container.Register<IOrdersService, OrdersService>(); container.RegisterMvcControllers(Assembly.GetExecutingAssembly()); container.Verify(); DependencyResolver.SetResolver( new SimpleInjectorDependencyResolver(container)); } }
There are some interesting things in the code above. First of all, calls to Register to tell Simple Injector that they need to return a specific implementation should be created when this abstraction is requested. Then Simple Injector allows you to register types with Web Request Lifestyle , which ensures that this instance will be deleted when the web request is completed (as in Application_EndRequest ). By calling RegisterMvcControllers , Simple Injector will register all the controllers for you. By providing SimpleInjectorDependencyResolver using the SimpleInjectorDependencyResolver , we enable MVC to route the creation of the controllers to the Simple Injector (as with the factory controller).
Although this code may be a little more difficult to understand at first, using the Injection Dependency container becomes really valuable when your application starts to grow. The DI container will help you maintain your root composition.