DevTeam / Pure.DI

Pure DI for .NET without frameworks!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Disposable Instances Handling

genment opened this issue · comments

Hi there,

I have a question regarding the disposable instances created by the DI. I've noticed that these disposable instances are only disposed automatically if their lifetime is set to singleton. Is this behavior intended or is it a bug?

Additionally, I've been exploring workarounds and came across this readme, but I'm unsure where to integrate the following code:

// Disposal of instances in reverse order
while (disposables.TryPop(out var disposable))
{
    disposable.Dispose();
}

Any guidance on where to incorporate this code would be greatly appreciated.

Thank you in advance for your assistance!

Hi @genment

Out of the box, it is possible to dispose instances with lifetime Singleton and Scoped. This is expected behavior, since the number of instances with other lifetime may be very large, may require a lot of memory to store them and a lot of CPU resources to register them in some collection.

And answering your second question, if instances with lifetimes Singleton and Scoped have strictly defined lifetimes, what are the lifetimes of other instances? It depends on the specific scenario. If you would describe your scenario in more detail, I would be able to give recommendations.

Added another example.

Thanks for replying.

I encountered a scenario where I wanted to bind a DownloadService from the Downloader package to DI with a Transient lifetime. Since the download functionality is only needed once in my project, it made sense to use Transient lifetime.

However, I ran into a problem: DownloadService implements IDisposable, and when I bind it with a Transient lifetime, its Dispose() method isn't called automatically.

After some consideration, I've decided to modify my approach. Instead of managing the DownloadService instance's lifetime via DI, I'm now using the using statement to handle its disposal.

Disposables are the hard to get right in DI. Autofac has a nice feature that deals with that
https://autofac.readthedocs.io/en/latest/advanced/owned-instances.html

Currently you have the option to inject a factory to your class and call

using(var instance = myfactory.Create())

and you need to make sure myfactory implementation will always creturn transient lifetime instance. If you return a singleton that will fuck up everything.

This is where Owned< T > comes into play by to ensure Owned Dispose method is no-op when the instance is registered with singleton lifetime.

@NikolayPianikov Owned< T > can be a great addition to Pure.DI

Disposables are the hard to get right in DI. Autofac has a nice feature that deals with that https://autofac.readthedocs.io/en/latest/advanced/owned-instances.html

Yeah, I read it, thanks. I've updated the example.

@NikolayPianikov Owned< T > can be a great addition to Pure.DI

Yes, the current approach looks complicated and not user friendly. I'll try to figure out a way to do something similar to Owned. The main difficulty is to make it as productive as possible in terms of speed and efficient in terms of memory consumption. There's also a question about using it in delegates. Since some instances (PerResolve) can be shared in a composition and it depends on the particular dependency tree.

A new feature has been added:

Example 1
Example 2

Still testing, but it'll look something like this. IAsynEnumerable will work the same way.

So there is also an extension point that allows you to work with other types, for example:

 .Accumulate<IMyDisposable, MyOwned>(Lifetime.Transient)

Another one example using custom accumulators

I am trying to figure out how Owned is implemented.

Lets say I have an assembly Library that has this class

internal class ServiceDescriptor
{
    public ServiceDescriptor(Func<Owned<IService>> owned)
    {

    }
}

and then in assembly Executable

        .Bind<IService>.To<MyService>()
        .Bind().To(ctx => 
        {
            ctx.Inject(out Func<Owned<IService>> fact);
            return new ServiceDescriptor(fact);
        })

I can see Owned class is generated by the source generator?
if so how the type Owned in Library assembly will be the same as the Executable assembly?

I am trying to use it with 2 assemblies and I am getting errors. I can see Owned class and interface are generated in both assemblies, which means they are different types.

I can see Owned class is generated by the source generator?

Yes, the Owned class is part of the Pure.DI API.

if so how the type Owned in Library assembly will be the same as the Executable assembly?

Yes you are right.

I am trying to use it with 2 assemblies and I am getting errors. I can see Owned class and interface are generated in both assemblies, which means they are different types.

I have created the issue #59 and will fix that soon.

I would suggest adding the reference to Pure.DI to the root assembly only and use your own accumulator to register the disposing objects, here is an example. This will allow your business entities to not know anything about DI.

#59 fixed in 2.1.22

The same question is about Tag, Type and Ordinal attributes. The Pure.DI API is supposed to be used only when configuring a composition, so the Pure.DI API is internal. It is recommended that other asmblies use their own Accumulators and their own attributes, this will make them completely independent of the infrastructure (DI).