Autofac:如何限制 IDisposable 对象的生命周期而不传递 IoC 容器
我目前正在学习如何使用 Autofac,并且我一直坚持确定性地处置 IDisposable
对象。在陈述我的问题之前,让我先介绍一下情况。
起始位置:
假设我的对象模型是通过以下接口定义的:
interface IApple : IDisposable
{
void Consume();
}
interface IHorse
{
void Eat(IApple apple); // is supposed to call apple.Consume()
}
interface IHorseKeeper
{
void FeedHorse(); // is supposed to call horse.Eat(apple)
// where 'horse' is injected into IHorseKeeper
// and 'apple' is generated by IHorseKeeper on-the-fly
}
此外,我定义了一个将用作 IApple
工厂的委托:
delegate IApple AppleFactory;
Autofac 配置:
现在,我将按如下方式注册上述类型-- 请注意,我省略了 Apple
和 Horse
两个类的代码,因为它们实现起来很简单:
var builder = new Autofac.ContainerBuilder();
builder.RegisterType<Apple>().As<IApple>();
builder.RegisterType<Horse>().As<IHorse>();
builder.RegisterType<HorseKeeper>().As<IHorseKeeper>();
builder.RegisterGeneratedFactory<AppleFactory>();
我的问题:
我不太知道如何实现方法IHorseKeeper.Feed
。这是我目前拥有的:
class HorseKeeper : IHorseKeeper
{
private readonly IHorse horse;
private readonly AppleFactory appleFactory;
public HorseKeeper(IHorse horse, AppleFactory appleFactory)
// ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
// constructor injection
{
this.horse = horse;
this.appleFactory = appleFactory;
}
public void FeedHorse()
{
using (var apple = appleFactory())
{
horse.Eat(apple);
} // <- Dispose() apple now (ASAP), as it's no longer needed!
}
}
这是我想要的代码,因为它完全与 Autofac 无关。只要 AppleFactory
按预期工作,它也可以与另一个 IoC 容器一起使用。
但是,由于 Autofac 为我处理 AppleFactory,它会跟踪为我生成的所有 IApple 对象,因此需要 Dispose它们本身在容器的生命周期结束时。即,生产出的苹果
将被处理两次。
我认为将 IApple
注册为 .ExternallyOwned()
不是可行的解决方案,因为在某些情况下,让 Autofac 处理 IApple
可能会更容易' 寿命。
使用 Autofac 进行确定性处置需要使用 container 创建嵌套容器。 BeginLifetimeScope()
,但是我不想在 HorseKeeper.FeedHorse
中使用它,因为这样 HorseKeeper
就会依赖于 Autofac,我想保留我的代码与 IoC 无关。
问题:
如何以与 IoC (Autofac) 无关的方式实现 HorseKeeper.FeedHorse
,同时确保正确处理动态生成的对象?
I'm currently learning how to use Autofac, and I'm stuck with disposing IDisposable
objects deterministically. Let me first present the situation before I'll state my problem.
Starting position:
Let's say my object model is defined through the following interfaces:
interface IApple : IDisposable
{
void Consume();
}
interface IHorse
{
void Eat(IApple apple); // is supposed to call apple.Consume()
}
interface IHorseKeeper
{
void FeedHorse(); // is supposed to call horse.Eat(apple)
// where 'horse' is injected into IHorseKeeper
// and 'apple' is generated by IHorseKeeper on-the-fly
}
Further, I define a delegate that will be used as an IApple
factory:
delegate IApple AppleFactory;
Autofac configuration:
Now, I would register the above types as follows -- note that I'm omitting the code of both classes Apple
and Horse
, since they're trivial to implement:
var builder = new Autofac.ContainerBuilder();
builder.RegisterType<Apple>().As<IApple>();
builder.RegisterType<Horse>().As<IHorse>();
builder.RegisterType<HorseKeeper>().As<IHorseKeeper>();
builder.RegisterGeneratedFactory<AppleFactory>();
My problem:
I don't quite know how to implement method IHorseKeeper.Feed
. Here's what I currently have:
class HorseKeeper : IHorseKeeper
{
private readonly IHorse horse;
private readonly AppleFactory appleFactory;
public HorseKeeper(IHorse horse, AppleFactory appleFactory)
// ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
// constructor injection
{
this.horse = horse;
this.appleFactory = appleFactory;
}
public void FeedHorse()
{
using (var apple = appleFactory())
{
horse.Eat(apple);
} // <- Dispose() apple now (ASAP), as it's no longer needed!
}
}
This is the kind of code I would like to have, as it's completely Autofac-agnostic. It could just as well work with another IoC container, as long as AppleFactory
works as expected.
However, because Autofac handles the AppleFactory
for me, it will keep track of all IApple
objects it produces for me, and will therefore want to Dispose
them itself at the end of the container's lifetime. Ie., the produced apple
will be disposed twice.
I suppose registering IApple
as .ExternallyOwned()
is no viable solution, as there might be cases where it's easier to let Autofac handle the IApple
s' lifetime.
Deterministic disposal with Autofac requires the creation of a nested container using container.BeginLifetimeScope()
, however I don't want to use this inside HorseKeeper.FeedHorse
because then HorseKeeper
becomes dependent on Autofac, and I would like to keep my code IoC-agnostic.
Question:
How do I implement HorseKeeper.FeedHorse
in a IoC (Autofac)-agnostic way while ensuring that on-the-fly generated objects are disposed properly?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这里的其他答案很有洞察力,但有一个问题。在这两种情况下,如果 Apple 有其他需要处理的依赖项,则不会发生正确的清理。
Autofac 2 提供了一个新功能来帮助解决这个问题,称为“拥有的实例”。我注意到您的注册代码是 Autofac 1.4,因此,如果您无法升级,请告诉我(还有其他不太透明的方法可以做到这一点。)
照常注册 Apple(不是外部所有):
将 AppleFactory 声明为
: Autofac 2,您不再需要调用 RegisterGenerateFactory() - 这是自动的。
然后,在 HorseKeeper 中,像这样喂马:(
注意 .Value 属性以获取底层 IApple。
在 using 块结束时,苹果及其所有依赖项将被清除。
使用 IApple 的任何其他组件直接(作为依赖项)将得到通常的行为。
The other answers here are insightful, but have a problem. In both cases, if Apple has other dependencies that need disposal, correct cleanup won't happen.
Autofac 2 provides a new feature to help here, called "owned instances". I noticed that your registration code is Autofac 1.4, so if you're unable to upgrade let me know (there are other, less transparent, ways to do this.)
Register Apple as usual (not externally owned):
Declare AppleFactory as:
In Autofac 2, you do not need to call RegisterGeneratedFactory() anymore - this is automatic.
Then, in HorseKeeper, feed the horse like this:
(Note the .Value property to get the underlying IApple.
At the end of the using block the apple, plus all of its dependencies, will be cleaned up.
Any other components that use IApple directly (as dependencies) will get the usual behaviour.
唯一的方法是使用
ExternallyOwned
修饰符修改 Apple 注册。这指示 Autofac 不要跟踪要处置的对象,而是让外部人员(您的代码)处理处置。但正如您所说,您现在必须确保手动处理 Apple 的所有实例,因为您不会从 Autofac 获得自动帮助。不过,通过此注册,您的 Feed 代码将按预期工作。
注意:关于接口是否应该继承
IDisposable
的讨论:IMO,当接口继承IDisposable
时,这是对“消费”开发人员认为该实例应该在某个时间点被处置。对于 IApple,由于该接口也是 IDisposable,因此开发人员应确保处置实例(并且必须也必须注册为ExternallyOwned)。另一方面,如果 Apple 类看起来像这样:IApple 的使用者现在完全不知道实例是 IDisposable 的事实。在这种情况下,我们将让容器处理处置。
所以我的结论是,作为 Apple 和 IApple 的开发者,我可以选择是要求消费者自行处理还是将其交给容器处理。
The only way is to modify the Apple registration with the
ExternallyOwned
modifier. This instructs Autofac to not track the object for disposal, but rather let someone external (your code) handle the disposal. But as you state, you will now have to make sure that all instances of Apple are disposed manually, since you will get no automatic help from Autofac.With this registration your Feed code will work as expected, though.
Note: on the discussion whether the interface should inherit
IDisposable
or not: IMO, when an interface inheritsIDisposable
, this is an indication to the "consuming" developer that the instance should be disposed at some point in time. In the case of IApple, since that interface is also IDisposable, the developer should make sure to dispose instances (and must also then be registered as ExternallyOwned). On the other hand, if the Apple class looked like this:consumers of the IApple is now fully unaware of the fact that instances is IDisposable. In this case we'll let the container handle disposal.
So my conclusion is that it is up to me as the developer of Apple and IApple to choose whether I'll require consumers to handle disposal or leave it up to a container.
如果您有时想自己管理 Apple 实例的生命周期,有时让容器处理它,那么您可以定义两个接口:
然后注册该类两次:
然后您可以将 DisposableAppleFactory 注入到需要创建和处置苹果的类中。
对于只需要与容器具有相同生命周期的苹果的类,您可以注入 IApple。
但是,您同时需要两者的事实可能表明您正在混合 新产品和可注射产品。 Apple 可能只是一个“可更新”对象,即不需要由 IoC 容器管理的对象。
If you sometimes want to manage the lifetime of Apple instances yourself, and sometimes let the container handle it, then you can define two interfaces:
And then register the class twice:
You can then inject a DisposableAppleFactory into classes that need to create and dispose apples.
For classes which just need an apple with the same lifetime as the container, you inject IApple instead.
However, the fact that you need both may indicate that you are mixing newables and injectables. Apple may simply be a "newable" object, i.e. one that doesn't need to be managed by the IoC container.