hadashiA / VContainer

The extra fast, minimum code size, GC-free DI (Dependency Injection) library running on Unity Game Engine.
https://vcontainer.hadashikick.jp
MIT License
1.96k stars 172 forks source link

Simple registration, how? #558

Open Bezarius opened 1 year ago

Bezarius commented 1 year ago

We have a kind of third-party plugin that is a provider for various plug-in subsystems. These subsystems are created by the provider itself and this provider manages their lifecycle.

Issue: if we register them in a container, the container will take over the lifecycle management of these dependencies. Which will cause Dispose to be called in the container and in a third-party provider. This is incorrect behavior, because in this situation the container is not the producer of the object and should not take responsibility for the release.

The question is, how to register so that the vContainer does not manage the lifecycle?

Example:

     // This class is third-party code
    public interface IClassA { }

    // This class is third-party code
    public class ClassA : IClassA, IDisposable
    {
        public void Dispose()
        {

        }
    }

    // This class is third-party code
    public interface IClassB { }

    // This class is third-party code
    public class ClassB : IClassB , IDisposable
    {
        public void Dispose()
        {

        }
    }

    // This class is third-party code
    public class ClassContainer : IDisposable
    {
        public IClassA ClassA => _classA;
        private ClassA _classA = new();
        public IClassB ClassB = new();
         private ClassB _classB = new();

        public void Dispose()
        {
            // ... here other inner disposables
            _classA .Dispose();
            _classB .Dispose();
        }
    }

And registrations, which naturally won't work:

 builder.Register<ClassContainer>(Lifetime.Scoped)
                .AsSelf()
                .AsImplementedInterfaces();

            // here I make registraion of `IClassA`, but `vcontainer` would look into all class interfaces and will register `IDisposable`, which will lead to a double `Dispose` call in the future
            builder
                .Register<IClassA>(x =>  x.Resolve<ClassContainer>().ClassA, Lifetime.Singleton)
                .AsSelf();

            builder
                .Register<IClassB>(x => x.Resolve<ClassContainer>().ClassB, Lifetime.Singleton)
                .AsSelf();

Injecting ClassContainer everywhere is obviously a bad idea.

IaroslavGusiev commented 1 year ago

Maybe just use constructor ?)))))))))))

pnarimani commented 1 year ago

Maybe just use constructor ?)))))))))))

Obviously this is sample. It's possible that OP cannot use ClassA and ClassB constructors due to them being third party code.

Bezarius commented 1 year ago

Obviously this is sample. It's possible that OP cannot use ClassA and ClassB constructors due to them being third party code.

Exactly.

IaroslavGusiev commented 1 year ago

Maybe just use constructor ?)))))))))))

Obviously this is sample. It's possible that OP cannot use ClassA and ClassB constructors due to them being third party code.

use factory!) Lol.

Bezarius commented 1 year ago

use factory!) Lol.

RegisterFactory obviously won't work either :\

IaroslavGusiev commented 1 year ago

use factory!) Lol.

RegisterFactory obviously won't work either :\

Main question here is that VContainer is focus on non-lazy instance creation. Anyway SetA and SetB should be good to inject from method. This move will encapsule logic, because injection from properties isn't good as it looks like. Anyway you can inject properties.

For such cases I use such extension - https://pastebin.com/irNEeivs

Bezarius commented 1 year ago

@IaroslavGusiev It seems to me that you don't understand the problem at all. I'm not trying to do an injection of a class properties or something like this :\ I'm trying to figure out how to register dependency from Resolve. Like this: https://github.com/hadashiA/VContainer/compare/master...Bezarius:VContainer:master Btw, for non-zazy initialization, it is enough to use IInitializable or IStartable.

hadashiA commented 1 year ago

Simple registration, how?

What is the problem with this issue?

You haven't explained it? What is wrong with the third party code?

Or maybe it's a different issue. Please include the issue title and description.

Bezarius commented 1 year ago

@hadashiA issue was updated

Valerio-Corso commented 1 year ago

If you just want to manually handle the lifecycle you can register the instance, as that is not managed by the container.

var b = builder.Resolve<ClassContainer>().ClassB;
builder.RegisterInstance<ClassB>(b).As<IClassB>();
Bezarius commented 1 year ago

@Valerio-Corso What you wrote here won't even compile :\ VContainer aren't Zenject.

Valerio-Corso commented 1 year ago

sorry my bad, that was from top of my head and syntax may be wrong but the idea of registering an instance stands.

Otherwise, not sure if these solution fits your case but what i would try:

Bezarius commented 1 year ago

@Valerio-Corso https://github.com/hadashiA/VContainer/pull/554 this is how it could work in my opinion.

Author rejected this pull, so there should be another way to solve this problem simply using existing options.

Lifetimescope have a different role, so the solution through them will be a bad idea. What should I do if I have a lot of such cases?

Register the class .Register but manually specify which interfaces you want and omit IDisposable, never tried this tbh.

If you look at the very first post, then the option is considered there and it is written why it will not work.

Bezarius commented 8 months ago

@hadashiA mb you need more context?

ArkTarusov commented 6 months ago

Perhaps you will find the answer at this link? https://vcontainer.hadashikick.jp/registering/register-type#register-instance

// ...
var obj = new ServiceA();
// ...

builder.RegisterInstance(obj);

Instances registered with RegisterInstance are not managed by the container.

  • Dispose will not be executed automatically.
  • Method Injection will not be executed automatically
hadashiA commented 6 months ago

Sorry, I doubt I understand the context well enough. But does this mean that the third-party plugin sets up a DI container and the application registers against it?

In my opinion, DI is not necessary for mere third-party plug-ins or libraries. Even if such a mechanism were used, the DI container should not be shared between the library application side and the library side.

Dependency inversion with a DI container is only for writing applications. The library is always the 'dependent party'. (By the same token, it is ridiculous to introduce a clean or layered architecture for a mere library.

The exception, of course, is frameworks. But in this case, the lifespan management should match, because it provides the application with the lifespan management of the framework.