C# 您如何协调 IDisposable 和 IoC?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/987761/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you reconcile IDisposable and IoC?
提问by Mr. Putty
I'm finally wrapping my head around IoC and DI in C#, and am struggling with some of the edges. I'm using the Unity container, but I think this question applies more broadly.
我终于在 C# 中围绕 IoC 和 DI 进行了思考,并且正在努力解决一些问题。我正在使用 Unity 容器,但我认为这个问题适用范围更广。
Using an IoC container to dispense instances that implement IDisposable freaks me out! How are you supposed to know if you should Dispose()? The instance might have been created just for you (and therefor you should Dispose() it), or it could be an instance whose lifetime is managed elsewhere (and therefor you'd better not). Nothing in the code tells you, and in fact this could change based on configuration!!! This seems deadly to me.
使用 IoC 容器来分配实现 IDisposable 的实例让我感到害怕!你怎么知道你是否应该 Dispose()?该实例可能是专门为您创建的(因此您应该 Dispose() 它),或者它可能是一个其生命周期在其他地方管理的实例(因此您最好不要)。代码中没有任何内容告诉您,实际上这可能会根据配置而改变!!!这对我来说似乎是致命的。
Can any IoC experts out there describe good ways to handle this ambiguity?
有没有 IoC 专家可以描述处理这种歧义的好方法?
采纳答案by Samuel Hyman
AutoFachandles this by allowing the creation of a nested container. When the container is finished with, it automatically disposes of all IDisposable objects within it. More here.
AutoFac通过允许创建嵌套容器来处理此问题。当容器完成时,它会自动处理其中的所有 IDisposable 对象。更多在这里。
.. As you resolve services, Autofac tracks disposable (IDisposable) components that are resolved. At the end of the unit of work, you dispose of the associated lifetime scope and Autofac will automatically clean up/dispose of the resolved services.
.. 在您解析服务时,Autofac 会跟踪已解析的一次性 (IDisposable) 组件。在工作单元结束时,您处理关联的生命周期范围,Autofac 将自动清理/处理已解决的服务。
回答by Paul Sonier
I think in general the best approach is to simply not Dispose of something which has been injected; you have to assume that the injector is doing the allocation and deallocation.
我认为一般来说最好的方法是不要处理已经注入的东西;您必须假设注入器正在执行分配和解除分配。
回答by Reed Copsey
This depends on the DI framework. Some frameworks allow you to specify whether you want a shared instance (always using the same reference) for every dependency injected. In this case, you most likely do not want to dispose.
这取决于 DI 框架。某些框架允许您为每个注入的依赖项指定是否需要共享实例(始终使用相同的引用)。在这种情况下,您很可能不想处理。
If you can specify that you want a unique instance injected, then you will want to dispose (since it was being constructed for you specifically). I'm not as familiar with Unity, though - you'd have to check the docs as to how to make this work there. It's part of the attribute with MEF and some others I've tried, though.
如果您可以指定要注入的唯一实例,那么您将需要处置(因为它是专门为您构建的)。不过,我对 Unity 不太熟悉 - 您必须查看文档以了解如何在那里完成这项工作。不过,这是 MEF 和我尝试过的其他一些属性的一部分。
回答by Mike Valenty
You definitely do not want to call Dispose() on an object that was injected into your class. You can't make the assumption that you are the only consumer. Your best bet is to wrap your unmanaged object in some managed interface:
您绝对不想在注入到您的类中的对象上调用 Dispose()。您不能假设您是唯一的消费者。最好的办法是将非托管对象包装在某个托管接口中:
public class ManagedFileReader : IManagedFileReader
{
public string Read(string path)
{
using (StreamReader reader = File.OpenRead(path))
{
return reader.ReadToEnd();
}
}
}
That is just an example, I would use File.ReadAllText(path) if I were trying to read a text file into a string.
这只是一个例子,如果我试图将文本文件读入字符串,我将使用 File.ReadAllText(path)。
Another approach is to inject a factory and manage the object yourself:
另一种方法是注入一个工厂并自己管理对象:
public void DoSomething()
{
using (var resourceThatShouldBeDisposed = injectedFactory.CreateResource())
{
// do something
}
}
回答by Konamiman
In the Unity framework, there are two ways to register the injected classes: as singletons (you get always the same instance of the class when you resolve it), or such as you get a new instance of the class on each resolution.
在 Unity 框架中,有两种方法可以注册注入的类:作为单例(当你解析它时你总是得到类的相同实例),或者你在每个分辨率上得到一个类的新实例。
In the later case, you have the responsibility of disposing the resolved instance once you don't need it (which is a quite reasonable approach). On the other hand, when you dispose the container (the class that handles object resolutions), all the singleton objects are automatically disposed as well.
在后一种情况下,您有责任在不需要时处理已解析的实例(这是一种非常合理的方法)。另一方面,当您处置容器(处理对象解析的类)时,所有单例对象也会自动处置。
Therefore, there are apparently no issues with injected disposable objects with the Unity framework. I don't know about other frameworks, but I suppose that as long as a dependency injection framework is solid enough, it for sure handles this issue in one way or another.
因此,使用 Unity 框架注入的一次性对象显然没有问题。我不知道其他框架,但我想只要依赖注入框架足够可靠,它肯定会以一种或另一种方式处理这个问题。
回答by Bas Hamer
Putting a facade in front of the container can resolve this as well. Plus you can extend it to keep track of a more rich life cycle like service shutdowns and startups or ServiceHost state transitions.
在容器前面放置一个外观也可以解决这个问题。此外,您可以扩展它以跟踪更丰富的生命周期,例如服务关闭和启动或 ServiceHost 状态转换。
My container tends to live in an IExtension that implements the IServiceLocator interface. It is a facade for unity, and allows for easy access in WCf services. Plus I have access to the service events from the ServiceHostBase.
我的容器倾向于存在于实现 IServiceLocator 接口的 IExtension 中。它是统一的外观,并允许在 WCf 服务中轻松访问。另外,我可以访问来自 ServiceHostBase 的服务事件。
The code you end up with will attempt to see if any singleton registered or any type created implements any of the interfaces that the facade keeps track of.
您最终得到的代码将尝试查看是否有任何注册的单例或创建的任何类型实现了外观跟踪的任何接口。
Still does not allow for the disposing in a timely manner as you are tied to these events but it helps a bit.
由于您与这些事件有关,因此仍然不允许及时处理,但它有点帮助。
If you want to dispose in a timely manner (aka, now v.s. upon service shutdown). You need to know that the item you get is disposable, it is part of the business logic to dispose of it, so IDisposable should be part of the interface of the object. And there probably should be verification of expectations untitests related to the dispose method getting called.
如果您想及时处理(又名,现在与服务关闭时)。你需要知道你得到的item是一次性的,它是业务逻辑的一部分来处理它,所以IDisposable应该是对象接口的一部分。并且可能应该验证与调用 dispose 方法相关的期望 untitests。
回答by Remco te Wierik
This has puzzled me frequently as well. Though not happy about it, I always came to the conclusion that never returning an IDisposable object in a transient way was best.
这也经常让我感到困惑。虽然对此并不满意,但我总是得出结论,永远不要以短暂的方式返回 IDisposable 对象是最好的。
Recently, I rephrased the question for myself: Is this really an IoC issue, or a .net framework issue? Disposing is awkward anyway. It has no meaningful functional purpose, only technical. So it's more a framework issue that we have to deal with, than an IoC issue.
最近,我为自己改写了这个问题:这真的是 IoC 问题,还是 .net 框架问题?反正处理起来很尴尬。它没有有意义的功能目的,只有技术目的。因此,与其说是 IoC 问题,不如说是我们必须处理的框架问题。
What I like about DI is that I can ask for a contract providing me functionality, without having to bother about the technical details. I'm not the owner. No knowledge about which layer it's in. No knowledge about which technologies are required to fulfil the contract, no worries about lifetime. My code looks nice and clean, and is highly testable. I can implement responsibilities in the layers where they belong.
我喜欢 DI 的一点是,我可以要求一份合同提供我的功能,而不必担心技术细节。我不是业主。不知道它在哪一层。不知道需要哪些技术来履行合同,不用担心生命周期。我的代码看起来漂亮干净,并且高度可测试。我可以在它们所属的层中实现职责。
So if there's an exception to this rule that does require me to organise the lifetime, let's make that exception. Whether I like it or not. If the object implementing the interface requires me to dispose it, I want to know about it since then I am triggered to use the object as short as possible. A trick by resolving it using a child container which is disposed some time later on might still cause me keeping the object alive longer than I should. The allowed lifetime of the object is determined when registering the object. Not by the functionality that creates a child container and holds on to that for a certain period.
所以如果这个规则有一个例外,确实需要我组织生命周期,让我们做那个例外。不管我喜不喜欢。如果实现接口的对象需要我处理它,我想知道它,从那时起我被触发尽可能短地使用该对象。使用稍后处理的子容器解决它的一个技巧可能仍然导致我保持对象的存活时间比我应该的更长。对象的允许生命周期是在注册对象时确定的。不是通过创建子容器并在一段时间内保留它的功能。
So as long as we developers need to worry about disposing (will that ever change?) I will try to inject as few transient disposable objects as possible. 1. I try to make the object not IDisposable, for example by not keeping disposable objects on class level, but in a smaller scope. 2. I try to make the object reusable so that a different lifetime manager can be applied.
因此,只要我们的开发人员需要担心处理(这会改变吗?),我将尝试注入尽可能少的临时一次性对象。1. 我尝试使对象不是 IDisposable,例如通过不在类级别上保留一次性对象,而是在较小的范围内。2. 我尝试使对象可重用,以便可以应用不同的生命周期管理器。
If this is not feasible, I use a factory to indicate that the user of the injected contract is owner and should take responsibility for it.
如果这不可行,我用一个工厂来表明注入合约的用户是所有者,应该对此负责。
There is one caveat: changing a contract implementer from non-disposable to disposable will be a breaking change. At that time the interface will no longer be registered, but the interface to the factory. But I think this applies to other scenario's as well. Forgetting to use a child container will from that moment on give memory issues. The factory approach will cause an IoC resolve exception.
有一个警告:将合同执行者从非一次性更改为一次性将是一个重大变化。那个时候接口将不再注册,而是接口到工厂。但我认为这也适用于其他场景。从那一刻起,忘记使用子容器会出现内存问题。工厂方法将导致 IoC 解析异常。
Some example code:
一些示例代码:
using System;
using Microsoft.Practices.Unity;
namespace Test
{
// Unity configuration
public class ConfigurationExtension : UnityContainerExtension
{
protected override void Initialize()
{
// Container.RegisterType<IDataService, DataService>(); Use factory instead
Container.RegisterType<IInjectionFactory<IDataService>, InjectionFactory<IDataService, DataService>>();
}
}
#region General utility layer
public interface IInjectionFactory<out T>
where T : class
{
T Create();
}
public class InjectionFactory<T2, T1> : IInjectionFactory<T2>
where T1 : T2
where T2 : class
{
private readonly IUnityContainer _iocContainer;
public InjectionFactory(IUnityContainer iocContainer)
{
_iocContainer = iocContainer;
}
public T2 Create()
{
return _iocContainer.Resolve<T1>();
}
}
#endregion
#region data layer
public class DataService : IDataService, IDisposable
{
public object LoadData()
{
return "Test data";
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
/* Dispose stuff */
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
#endregion
#region domain layer
public interface IDataService
{
object LoadData();
}
public class DomainService
{
private readonly IInjectionFactory<IDataService> _dataServiceFactory;
public DomainService(IInjectionFactory<IDataService> dataServiceFactory)
{
_dataServiceFactory = dataServiceFactory;
}
public object GetData()
{
var dataService = _dataServiceFactory.Create();
try
{
return dataService.LoadData();
}
finally
{
var disposableDataService = dataService as IDisposable;
if (disposableDataService != null)
{
disposableDataService.Dispose();
}
}
}
}
#endregion
}