Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to obtain a lifetime equivalent of DryIoc's ScopedTo<type> #827

Open
Geminior opened this issue Jun 25, 2020 · 7 comments
Open

How to obtain a lifetime equivalent of DryIoc's ScopedTo<type> #827

Geminior opened this issue Jun 25, 2020 · 7 comments
Labels
Milestone

Comments

@Geminior
Copy link

Geminior commented Jun 25, 2020

Certain other DI containers provide the notion of a scope that is bound to a specific type instance, such that when the instance is created, all dependencies are resolved in the scope of that instance (provided the dependencies where registered with this type of scoped lifetime).

In some scenarios, a type acts as a natural scope in an application, and in such scenarios this type of scope is useful.

Grabbed and slightly modified from DryIoc docs:

    class Foo
    {
        public SubDependency Sub { get; }
        public Dependency Dep { get; }
        public Foo(SubDependency sub, Dependency dep)
        {
            Sub = sub;
            Dep = dep;
        }
    }

    class Dependency
    {
        public SubDependency Sub { get; }
        public Dependency(SubDependency sub)
        {
            Sub = sub;
        }
    }

    class SubDependency { }
        var container = new Container();

        // This is required to mark that `Foo` opens the scope
        container.Register<Foo>(setup: Setup.With(openResolutionScope: true));

        container.Register<Dependency>();
        container.Register<SubDependency>(Reuse.ScopedTo<Foo>());

        var foo1 = container.Resolve<Foo>();
        var foo2 = container.Resolve<Foo>();

        Assert.AreNotSame(foo1, foo2);
        Assert.AreSame(foo1.Sub, foo1.Dep.Sub);
        Assert.AreNotSame(foo1.Sub, foo2.Sub);

So the main feature here is that the container manages the scope associated with each Foo instance.
Typically you will want a reference to that specific scope, so that it can be disposed along side the instance it's associated with, or if that is not possible, create a scope explicitly and resolve the root from there, and dispose of both together (not the responsibility of the container).

Is there a way to achieve this behaviour in Simple Injector, and if not, would it perhaps be a meaningful addition?

@dotnetjunkie
Copy link
Collaborator

dotnetjunkie commented Jun 25, 2020

There is no simple way to achieve this with Simple Injector, i.e. there is no built-in feature that you can use to achieve this. There are some extension points though that might allow you to plugin this behavior, but that would take some time for me to figure out how to achieve this.

In Simple Injector, the unit for scoping is the Scope class. Scoped registrations are always scoped to some Scope instance. Most DI Containers work this way.

What is your specific use case for this? Why doesn't scoping using Scope instances work for you?

@Geminior
Copy link
Author

Geminior commented Jun 25, 2020

Scoping using Scope instances may very well work fine for our needs.
Specifically we have a type that is a natural scope for many services so being able to explicitly state that in the registration, appeared to be a useful feature.

However scoping using and outer Scope instance will still be required to properly manage the auto created scope's lifetime, and after that realization the usefulness dropped quite a bit.

Currently I am simply gathering info in order to select the most appropriate DI container for our needs.

Thanks for taking the time to answer this.

@dotnetjunkie
Copy link
Collaborator

dotnetjunkie commented Jun 25, 2020

I decided to reopen your issue, because it allows me to track it as feature request, and, as you stated, it might be a meaningful addition.

@dotnetjunkie dotnetjunkie reopened this Jun 25, 2020
@dotnetjunkie dotnetjunkie added this to the Backlog milestone Jun 25, 2020
@dadhi
Copy link

dadhi commented Jun 25, 2020

Sorry for intruding.
What DryIoc doing is just a thin abstraction layer wrapping the following expression for Foo:

ctx => ctx.OpenScope(
    new ScopeName(typeof(Foo)), trackScopeInParent: true)
    .Resolve<Foo>();

I imagine you can do this manually if SimpleInjector supports the named / tagged scopes.

Update: The important thing to be aware of is tracking the scope in the parent scope or singleton scope.

@dotnetjunkie
Copy link
Collaborator

dotnetjunkie commented Jun 25, 2020

Hi @dadhi,

Sorry for intruding.

No problem. Thank you for chiming in.

@dotnetjunkie
Copy link
Collaborator

dotnetjunkie commented Jun 25, 2020

@dadhi, although I perhaps don't yet see what the use cases are for this feature, I i'm intrigued by this. And questions start to pop up in my mind. For instance, what happens when the 'scoped to' registration (SubDependency in the example) is injected in a graph where the specified 'parent scope' object is not a consumer (Foo in the example)? Does the registration get scoped to the already existing scope? Do you throw an exception? Or does the registration become transient/singleton?

@dadhi
Copy link

dadhi commented Jun 25, 2020

what happens when the 'scoped to' registration (SubDependency in the example) is injected in a graph where the specified 'parent scope' object is not a consumer

Then it won't be injected (will lead to exception depending on the policy) because the container did not found the scope with the specified name (the name of the service Foo in this example).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants