我的典型DAL类看起来像这样:
public class OrdersRepository : IOrdersRepository,IDisposable { private IDbConnection _db; public OrdersRepository(IDbConnection db) // constructor { _db = db; } public void Dispose() { _db.Dispose(); } }
public class ordeRSService : IDisposable { IOrdersRepository _orders; public ordeRSService() : this(new OrdersRepository(new Ajx.Dal.DapperConnection().getConnection())) { } public ordeRSService(OrdersRepository ordersRepo) { _orders = ordersRepo; } public void Dispose() { _orders.Dispose(); } }
最后在我的UI层中,这就是我访问服务层的方式:
public class OrdersController : Controller,IDisposable { // // GET: /Orders/ private ordeRSService _orderService; public OrdersController():this(new ordeRSService()) { } public OrdersController(ordeRSService o) { _orderService = o; } void IDisposable.Dispose() { _orderService.Dispose(); } }
这一切都很好.但正如你所看到的,我依赖于每一层中的IDisposable. UI配置服务对象然后服务对象配置DAL对象然后DAL对象配置数据库连接对象.
我相信必须有更好的方法.我担心用户可能会忘记处理我的服务对象(在UI中),最终会出现许多开放式数据库连接或更糟糕的情况.请告知最佳做法.我需要一种方法来自动处理我的数据库连接或任何其他非托管资源(文件等).
解决方法
he who has ownership of the resource,should dispose it.
虽然可以转让所有权,但通常不应该这样做.在您的情况下,IDbConnection的所有权从ordeRSService传递到OrdersRepository(因为OrdersRepository处理连接).但在许多情况下,OrdersRepository无法知道是否可以处理连接.它可以在整个对象图中重用.因此,通常,您不应该通过构造函数处理传递给您的对象.
另一个问题是依赖关系的使用者通常无法知道依赖关系是否需要处理,因为是否需要处理依赖关系是实现细节.该信息可能在界面中不可用.
因此,请将OrdersRepository重构为以下内容:
public class OrdersRepository : IOrdersRepository { private IDbConnection _db; public OrdersRepository(IDbConnection db) { _db = db; } }
由于OrdersRepository不占用所有权,因此IDbConnection不需要配置IDbConnection,也不需要实现IDisposable.这明确地将处理连接的责任转移到OrdeRSService.但是,ordeRSService本身不需要IDbConnection作为依赖项;它只取决于IOrdersRepository.那么为什么不将构建对象图的责任从OrdeRSService中移除:
public class OrdeRSService : IDisposable { private readonly IOrdersRepository _orders; public ordeRSService(IOrdersRepository ordersRepo) { _orders = ordersRepo; } }
由于ordeRSService无需自行处理,因此无需实现IDisposable.而且由于它现在只有一个构造函数来获取它所需的依赖项,因此该类变得更容易维护.
因此,这将创建对象图的责任转移到OrdersController.但我们也应该将相同的模式应用于OrdersController:
public class OrdersController : Controller { private ordeRSService _orderService; public OrdersController(ordeRSService o) { _orderService = o; } }
同样,这个类变得更容易掌握,并且它不需要处理任何东西,因为它没有或拥有任何资源.
当然我们只是移动并推迟了我们的问题,因为显然我们仍然需要创建我们的OrdersController.但不同之处在于,我们现在将构建对象图的责任转移到应用程序中的单个位置.我们称这个地方为Composition Root.
依赖注入框架可以帮助您使组合根可维护,但即使没有DI框架,您也可以通过实现自定义ControllerFactory在MVC中轻松构建对象图:
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 (...) { // other controller here. } else { return base.GetControllerInstance(requestContext,controllerType); } } public static void CleanUpRequest() } var items = (List<IDisposable>)HttpContext.Current.Items["resources"]; if (items != null) items.ForEach(item => item.Dispose()); } private static T Disposable<T>(T instance) where T : IDisposable { var items = (List<IDisposable>)HttpContext.Current.Items["resources"]; if (items == null) { HttpContext.Current.Items["resources"] = items = new List<IDisposable>(); } items.Add(instance); return instance; } }
您可以将自定义控制器工厂挂钩到MVC应用程序的Global asax中,如下所示:
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(); } }
当然,使用依赖注入框架时,这一切都变得更加容易.例如,当您使用Simple Injector(我是Simple Injector的主要开发人员)时,您可以使用以下几行代码替换所有这些:
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)); } }
上面的代码中有一些有趣的事情发生了.首先,在请求给定抽象时,应该创建对Register告诉Simple Injector它们需要返回某个实现的调用.接下来,Simple Injector允许使用Web Request Lifestyle注册类型,这确保在Web请求结束时处理给定实例(就像我们在Application_EndRequest中所做的那样).通过调用RegisterMvcControllers,Simple Injector将为您批量注册所有控制器.通过向SimpleInjectorDependencyResolver提供MVC,我们允许MVC将控制器的创建路由到Simple Injector(就像我们对控制器工厂所做的那样).
虽然这个代码起初可能有点难以理解,但是当应用程序开始增长时,使用依赖注入容器变得非常有价值. DI容器将帮助您保持组合根的可维护性.