我的应用程序使用 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 类(注入(inject)数据库连接):
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 .依赖注入(inject)框架可以帮助您使您的组合根可维护,但即使没有 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 中 Hook 您的自定义 Controller 工厂:
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();
}
}
当然,当您使用依赖注入(inject)框架时,这一切都会变得容易得多。例如,当您使用 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 将为您批量注册所有 Controller 。通过为 MVC 提供 SimpleInjectorDependencyResolver
我们允许 MVC 将 Controller 的创建路由到 Simple Injector(就像我们对 Controller 工厂所做的那样)。尽管此代码一开始可能有点难以理解,但是当您的应用程序开始增长时,依赖注入(inject)容器的使用变得非常有值(value)。 DI 容器将帮助您保持您的 Composition Root 可维护。
关于c# - 正确自动处理 Sql 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22898873/