Reputation: 905
Like the below code, I have to use the factory/func way of registration with container as I don't know the value of "userId" property ahead of time but only during runtime. This is working & no issues with functionality aspects.
container.AddSingleton<IBLogic, BLogic>(); //explicitly registration 'BLogic' service object
container.AddSingleton<Func<string, IDBCache>>
(p=> (userId) => new IDBCache(userId, p.GetService<IBLogic>()));
Since here I'm using "new IDBCache", as per the link framework does not dispose of the services automatically.
Questions:
To make framework to dispose of the services automatically, is there any way out?
container.AddSingleton<Func<string, IDBCache>> (p=> (userId) => new IDBCache(userId, p.GetService()));
Since I'm just registering the definition of func/factory not the service object(like 'BLogic') as such, does AddSingleton provides any advantage over using 'AddScoped' or 'AddTransisent' like below?
container.AddTransisent<Func<string, IDBCache>>
(p=> (userId) => new IDBCache(userId, p.GetService<IBLogic>()));
Upvotes: 2
Views: 1980
Reputation: 172606
There's no easy fix for this. MS.DI doesn't contain a RegisterForDisposal
method that you can call. You will have to create a separate wrapper that implements disposable to which you can hook your created instances. For instance:
services.AddSingleton<IBLogic, BLogic>();
services.AddScoped<DisposeWrapper>();
services.AddScoped<Func<string, IDBCache>>(p => (userId) =>
{
var cache = new IDBCache(userId, p.GetRequiredService<IBLogic>());
p.GetRequiredService<DisposeWrapper>().Add(cache);
return cache;
});
Where the DisposeWrapper
looks like this:
public sealed class DisposeWrapper : List<IDisposable>, IDisposable
{
public void Dispose()
{
// Dispose in reverse order as creation.
for (int i = this.Count - 1; i >= 0; i--) this[i].Dispose();
}
}
Such implementation, however, comes with quite some caveats, because DisposeWrapper
should be registered using the same scope as you want to cache your IDBCache
. That's why in the example above, I registered both DisposeWrapper
and the factory as Scoped
.
Registering DisposeWrapper
as Singleton, means that created IDBCache
instances will only get disposed when the application ends. If you create many IDBCache
instances during the lifetime of the application, it will cause a memory leak. This is unlikely a good idea in your scenario.
Registering both as transient, on the other hand, could still (confusingly) cause memory leaks, because the factory could be injected into a singleton, causing it (and its DisposeWrapper
) to become a Captive Dependency.
Because of the complications of this design, I recommend changing your design.
If you redesign IDBCache
in such way that runtime data isn't required during object construction, you can allow IDBCache
to be registered in the container without factory (e.g. AddTransient<IDBCache>()
) which allows it to be disposed normally by the container. This will remove all above-introduced complexity.
Upvotes: 3