我的应用程序使用3层:DAL / Service / UL。
我的典型DAL类看起来像这样:
public class OrdersRepository : IOrdersRepository, IDisposable
{
private IDbConnection _db;
public OrdersRepository(IDbConnection db) // constructor
{
_db = db;
}
public void Dispose()
{
_db.Dispose();
}
}
我的服务像这样调用DAL类(注入数据库连接):
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中),最终会出现许多开放式数据库连接或更糟糕的情况。请告知最佳做法。我需要一种方法来自动处理我的数据库连接或任何其他非托管资源(文件等)。
答案 0 :(得分:10)
您的问题又回到了所有权原则:
拥有资源所有权的人应该将其处置掉。
虽然可以转让所有权,但通常不应该这样做。在您的情况下,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应用程序的全局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将为您批量注册所有控制器。通过向MVC提供SimpleInjectorDependencyResolver
,我们允许MVC将控制器的创建路由到Simple Injector(就像我们对控制器工厂所做的那样)。
虽然这个代码起初可能有点难以理解,但是当您的应用程序开始增长时,使用依赖注入容器变得非常有价值。 DI容器将帮助您保持组合根的可维护性。
答案 1 :(得分:0)
好吧,如果您对此非常偏执,可以使用终结器(析构函数)在对象被垃圾回收时自动执行Dispose。查看此链接,基本上解释了您需要了解的IDisposable,如果只想快速了解如何操作,请跳到示例部分。终结器(析构函数)是~MyResource()方法。
但无论如何,您应该始终鼓励库的使用者正确使用Dispose。通过垃圾收集器自动清理仍然存在缺陷。你不知道什么时候垃圾收集器会完成它的工作,所以如果你把很多这些类实例化并使用,那么在很短的时间内就会忘记你可能仍然遇到麻烦。