admin管理员组文章数量:1122846
I am using DryIoc, but I think this is a general question applicable to any IoC container.
Consider a singleton service(Let's call it SingletonService
). The SingletonService
needs a new set of objects every time one of its functions is called. For simplicity let's say there is only one object and is called WorkerService
. WorkerService
will use unmanaged resources and therefor needs to be disposed.
I am unclear how to make sure that the WorkerService
will be disposed as soon as it is not needed anymore. To have a reference to the container in SingletonService
is a no go as far as I understand, because of the service locator anti-pattern.
One solution that comes to mind is to inject a WorkerServiceFactory
into SingletonService
. WorkerServiceFactory
gets the container via injection. The factory then uses the container to get WorkerService
and returns it to SingletonService. Is SingletonService
the one responsible for disposing of WorkerService
? Isn't there a guideline that states that objects created in the container should be disposed by the container? Alternatively I could use a scope but I am unclear how. If I were to create a new scope in SingletonService
it would still contain a reference to the container(at least in DryIoc unless I am missing something). Creating the scope in the factory is meaningless since the scope would only be accessible by the factory.
How do you handle situations like this?
Edit 1:
I though about it a little more and came up with this:
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;
}
}
Essentially I am returning the WorkerService
with an out parameter which allows me to return the scope created inside of my factory to the caller. This way SingletonService
only cares about disposing the scope returned from the factory and the container handles everything else. Is this a sound approach?
I am using DryIoc, but I think this is a general question applicable to any IoC container.
Consider a singleton service(Let's call it SingletonService
). The SingletonService
needs a new set of objects every time one of its functions is called. For simplicity let's say there is only one object and is called WorkerService
. WorkerService
will use unmanaged resources and therefor needs to be disposed.
I am unclear how to make sure that the WorkerService
will be disposed as soon as it is not needed anymore. To have a reference to the container in SingletonService
is a no go as far as I understand, because of the service locator anti-pattern.
One solution that comes to mind is to inject a WorkerServiceFactory
into SingletonService
. WorkerServiceFactory
gets the container via injection. The factory then uses the container to get WorkerService
and returns it to SingletonService. Is SingletonService
the one responsible for disposing of WorkerService
? Isn't there a guideline that states that objects created in the container should be disposed by the container? Alternatively I could use a scope but I am unclear how. If I were to create a new scope in SingletonService
it would still contain a reference to the container(at least in DryIoc unless I am missing something). Creating the scope in the factory is meaningless since the scope would only be accessible by the factory.
How do you handle situations like this?
Edit 1:
I though about it a little more and came up with this:
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;
}
}
Essentially I am returning the WorkerService
with an out parameter which allows me to return the scope created inside of my factory to the caller. This way SingletonService
only cares about disposing the scope returned from the factory and the container handles everything else. Is this a sound approach?
1 Answer
Reset to default 1This looks like an example of what Mark Seemann has named the Captive Dependency problem. That article explores the problem well, but, albeit via skim-reading, I cannot see any suggestions there on how to address it.
I've used Castle.Windsor extensively in the past. It has a feature called the Typed Factory Facility. I'm not suggesting you start using Castle.Windsor for this feature, but I think it's a pattern that gives you a possible answer to your question. TBH, you've practically arrived at yourself anyway.
It provides container-aware factory implementations using an interface-based convention. So it's essentially the same as your IWorkerServiceFactory
. The key bit is the section on releasing the components you resolve via the factory. My perception of your current solution is that returning the container scope to the caller is giving the caller too much knowledge of the underlying infrastructure. The Typed Factory approach abstracts the lifespan management via the 'release/destroy' method on the interface. This way, the caller is responsible for the component's clean-up, but the mechanism is opaque. I would suggest doing something similar with your factory and encapsulating/hiding the underlying container mechanisms.
It looks like the Typed Factory feature also has support for IDisposable
, but this appears to operate at the factory level. I'm not sure how I'd implement my own equivalent of that, but that doesn't mean you should overlook it.
So, something like:
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);
}
}
本文标签: cHow to handle disposal of objects when using DIIoC containerStack Overflow
版权声明:本文标题:c# - How to handle disposal of objects when using DIIoC container? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736307202a1933228.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
var nestedScopedContainer = scopedContainer.OpenScope()
– mjwills Commented Nov 21, 2024 at 22:18WorkerService
withBackgroundService
you'll see there's an entire documentation page about this Use scoped services within a BackgroundService. Either you use a factory with ausing
block or you create scopes inside the Singleton as needed. It's always the responsibility of the consumer (SingletonService) to dispose either the service or the scope – Panagiotis Kanavos Commented Nov 22, 2024 at 9:17