你的問題又回到了當家作主的原則:
他誰擁有資源的所有權,應釋放它。
雖然所有權可以轉讓,但您通常不應該這樣做。在你的情況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();
}
}
當然,當您使用依賴注入框架這一切變得更加容易。例如,當您使用簡單的注射器(我是簡單的噴油器的鉛DEV),你可以用下面的代碼幾行替換這一切:
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將控制器的創建路由到簡單注入器(就像我們在控制器工廠那樣)。
雖然這段代碼起初可能有點難以理解,但當您的應用程序開始增長時,使用依賴注入容器變得非常有價值。 DI容器將幫助您保持Composition Root可維護。
我已經看了Finalizer,並且我注意到它有時並沒有在我的服務層對象中激發。我正在尋找更強大的解決方案,可能通過使用Ioc或DI。但我從來沒有用過它們,也不知道從哪裏開始。 – highwingers
如果它沒有觸發,那麼你的對象不會被垃圾回收清除,你可能有一個內存韭菜。如果通過設計某些實例,保留很長時間,那麼也許你應該重新思考你如何使用它們。 –
如果該類需要直接處理本機資源,則只應在類中實現終結器。在這種情況下,實現一個終結器是無用的,因爲'DbConnection'本身已經有一個終結器。 – Steven