我正在使用 DryIoc,但我认为这是一个适用于任何 IoC 容器的普遍问题。
考虑一个单例服务(我们称之为
SingletonService
)。每次调用 SingletonService
的一个函数时,都需要一组新的对象。为了简单起见,我们假设只有一个对象,称为 WorkerService
。 WorkerService
将使用非托管资源,因此需要进行处置。
我不清楚如何确保
WorkerService
在不再需要时立即被处理掉。据我所知,由于服务定位器反模式,在 SingletonService
中引用容器是不行的。
我想到的一个解决方案是将
WorkerServiceFactory
注入 SingletonService
。 WorkerServiceFactory
通过注入获取容器。然后工厂使用容器获取WorkerService
并将其返回给SingletonService。 SingletonService
是负责处置 WorkerService
的人吗?是否有一个准则规定在容器中创建的对象应该由容器处理?或者我可以使用范围,但我不清楚如何使用。如果我要在 SingletonService
中创建一个新范围,它仍然会包含对容器的引用(至少在 DryIoc 中,除非我遗漏了一些东西)。在工厂中创建作用域是没有意义的,因为该作用域只能由工厂访问。
遇到这样的情况你会如何处理?
编辑1:
我想了一些,然后想出了这个:
using DryIoc;
using JetBrains.Annotations;
using var container = new Container();
container.Register<ISingletonService, SingletonService>(reuse: Reuse.Singleton);
container.Register<IWorkerService, WorkerService>(reuse: Reuse.Scoped);
container.Register<IWorkerServiceFactory, WorkerServiceFactory>(reuse: Reuse.Singleton);
var singleton = container.Resolve<ISingletonService>();
singleton.PrintData();
singleton.PrintData();
singleton.PrintData();
singleton.PrintData();
singleton.PrintData();
interface ISingletonService
{
void PrintData();
}
class SingletonService(IWorkerServiceFactory workerFactory) : ISingletonService
{
public void PrintData()
{
using var scope = workerFactory.Create(out var worker);
Console.WriteLine(worker.GetData());
}
}
interface IWorkerService
{
string GetData();
}
class WorkerService : IWorkerService, IDisposable
{
public void Dispose()
{
Console.WriteLine("WorkerService was disposed");
}
public string GetData()
{
return "Hello Data";
}
}
interface IWorkerServiceFactory
{
[MustDisposeResource]
IDisposable Create(out IWorkerService service);
}
class WorkerServiceFactory(IResolverContext ctx) : IWorkerServiceFactory
{
public IDisposable Create(out IWorkerService service)
{
var scope = ctx.OpenScope();
service = scope.Resolve<IWorkerService>();
return scope;
}
}
本质上,我使用 out 参数返回
WorkerService
,它允许我将工厂内部创建的范围返回给调用者。这样 SingletonService
只关心处理从工厂返回的范围,容器处理其他所有事情。这是一个合理的方法吗?
这看起来像是 Mark Seemann 所说的“强制依赖”问题的一个例子。那篇文章很好地探讨了这个问题,但是,尽管是通过略读,我看不到任何关于如何解决它的建议。 我过去广泛使用过
Castle.Windsor。它有一个称为“类型化工厂设施”的功能。我并不是建议您开始使用 Castle.Windsor 来实现此功能,但我认为这是一种模式,可以为您的问题提供可能的答案。说实话,无论如何,你实际上已经找到了自己。
它使用基于接口的约定提供容器感知工厂实现。所以它本质上与你的IWorkerServiceFactory
相同。关键部分是“释放”您通过工厂解析的组件部分。我对当前解决方案的看法是,将容器范围返回给调用者会让调用者对底层基础设施了解太多。类型化工厂方法通过接口上的“释放/销毁”方法抽象了生命周期管理。这样,调用者负责组件的清理,但机制是不透明的。我建议对你的工厂做类似的事情并封装/隐藏底层容器机制。
看起来类型化工厂功能也支持
IDisposable
,但这似乎是在工厂级别运行的。我不确定我将如何实现我自己的等效项,但这并不意味着您应该忽略它。
所以,类似:
interface IWorkerServiceFactory
{
IWorkerService Create();
void Release(IWorkerService workerService);
}
class WorkerServiceFactory(IResolverContext ctx) : IWorkerServiceFactory
{
public IWorkerService Create()
{
// I don't know DryIoc, so I'm guessing...
return ctx.Resolve<IWorkerService>();
}
public void Release(IWorkerService workerService)
{
return ctx.Release(workerService);
}
}