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

Scope is lost in diposable service #678

Open
Ditriksss opened this issue Feb 25, 2025 · 6 comments
Open

Scope is lost in diposable service #678

Ditriksss opened this issue Feb 25, 2025 · 6 comments

Comments

@Ditriksss
Copy link

Ditriksss commented Feb 25, 2025

Hey, we have following issue:

Lets say, you have service ServiceC<Strategy> which is disposable, so you resolve it withusing. Inside using you create scope with _container.OpenScope();. Inside scope you resolve new instance of ServiceC<Strategy> but you want to reuse some service from parent scope.
And now is the issue. Injecting services are not provided from scope. Hovewer if you inject IResolver and use Resolve method to create Context class (which is set in parent scope and pass to child scope) var resolvedContext = resolver.Resolve<Context>(); everything works. But the same Context injected via constructor does not have set value.

So the problem is that in the ServiceC<TContext> constructor (and child classes conctructors) - Context is not provided from the current scope. Based on the below code Context should have Value == "value" in ServiceB.Do method.

Code to reproduce:

[TestFixture]
public class GHIssue678_Scope_is_lost_in_diposable_service
{
    [Test]
    public void Original_case()
    {
        IServiceProvider serviceProvider = null;

        var host = Host.CreateDefaultBuilder()
            .ConfigureServices(services =>
            {
                serviceProvider = new Container()
                    .WithDependencyInjectionAdapter(services)
                    .ConfigureServiceProvider<CompositionRoot>();
            })
            .Build();

        using var scope = serviceProvider.CreateScope();

        var service = scope.ServiceProvider.GetRequiredService<ServiceA>();
        service.Do();
    }

    public class CompositionRoot
    {
        public CompositionRoot(IRegistrator registrator)
        {
            registrator.Register<Context>(reuse: Reuse.Scoped);
            registrator.Register<Strategy>();
            registrator.Register<ServiceC<Strategy>>();
            registrator.Register<ServiceB>();
            registrator.Register<ServiceA>();
        }
    }

    public class Context
    {
        public string Value { get; set; }
    }

    public class Strategy
    {
        public Strategy(Context context, IResolver resolver)
        {
            var resolvedContext = resolver.Resolve<Context>();

            ContextFromConstructor = context;
            ContextResolveWithIResolver = resolvedContext;
        }

        public Context ContextFromConstructor { get; }

        public Context ContextResolveWithIResolver { get; }
    }

    public class ServiceC<TContext> : IDisposable
    {
        public ServiceC(Strategy strategy, Context context)
        {
            Strategy = strategy;
            Context = context;
        }

        public Strategy Strategy { get; }

        public Context Context { get; }

        public void Dispose()
        {
        }
    }

    public class ServiceB
    {
        private readonly IContainer _container;
        private readonly Context _context;

        public ServiceB(IContainer container, Context context)
        {
            _container = container;
            _context = context;
        }

        public void Do()
        {
            using var scope = _container.OpenScope();
            scope.Use(_context);

            var context = scope.Resolve<Context>();
            Assert.That(context.Value, Is.EqualTo("value"));

            var strategy = scope.Resolve<Strategy>();
            Assert.That(strategy.ContextResolveWithIResolver.Value, Is.EqualTo("value"));
            Assert.That(strategy.ContextFromConstructor.Value, Is.EqualTo("value"));

            using var serviceC = scope.Resolve<ServiceC<Strategy>>();
            Assert.That(serviceC.Strategy.ContextResolveWithIResolver.Value, Is.EqualTo("value"));
            Assert.That(serviceC.Strategy.ContextFromConstructor.Value, Is.EqualTo("value"));
            Assert.That(serviceC.Context.Value, Is.EqualTo("value"));
        }
    }

    public class ServiceA
    {
        private readonly IContainer _container;
        private readonly Context _context;
        private readonly ServiceB _serviceB;

        public ServiceA(IContainer container, Context context, ServiceB serviceB)
        {
            _container = container;
            _context = context;
            _serviceB = serviceB;
        }

        public void Do()
        {
            using var serviceC = _container.Resolve<ServiceC<Strategy>>(); // this cause the issue, please comment this line to resolve problem

            _context.Value = "value";

            _serviceB.Do();
        }
    }
}
@dadhi
Copy link
Owner

dadhi commented Feb 25, 2025

Thanks, will check later.

@Ditriksss
Copy link
Author

Hey @dadhi. Have you had chance to take a look ? We are looking for any solution, even workaround.

@dadhi
Copy link
Owner

dadhi commented Mar 3, 2025

@Ditriksss Looking at it now

dadhi added a commit that referenced this issue Mar 3, 2025
@dadhi
Copy link
Owner

dadhi commented Mar 3, 2025

Hi @Ditriksss
I have converted your code into the passing test and pushed it here 3840dfd

It is passing just fine.
As your setup is quite complex, could you add the required Asserts for the issue you're checking?

Btw, the repo is on the latest version of the DryIoc v6.0.0-preview-09 and DryIoc.Microsoft.DependencyInjection v8.0.0-preview-04
So, I would suggest to at least use those for the test.

@Ditriksss
Copy link
Author

Ditriksss commented Mar 4, 2025

Hej @dadhi
I added asserts in ServiceB.Do() method. Please take a look at main post here - I edited the test there.
I wasn't able to use preview versions - I was getting a lot of errors coming from DryIoc lib, no idea why.
Please run, on my computer it is failing.

One insight here - if you comment line
using var serviceC = _container.Resolve<ServiceC<Strategy>>();
test should pass - but in our example, such logic is required.

dadhi added a commit that referenced this issue Mar 5, 2025
@dadhi
Copy link
Owner

dadhi commented Mar 5, 2025

@Ditriksss Thanks for adding the missing code and Asserts. I am able to reproduce the issue now.

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

No branches or pull requests

2 participants