dadhi / DryIoc

DryIoc is fast, small, full-featured IoC Container for .NET
MIT License
1.01k stars 123 forks source link

IDisposable dependencies does not get disposed when web request ends #627

Closed Madajevas closed 8 months ago

Madajevas commented 8 months ago

Hey. I have the following in .NET 4.8 WEB API project:

// disposable dependency
public interface ITheDependency { }
class TheDependency : ITheDependency, IDisposable
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }
}

// WebApiConfig.cs.Register
var container = new Container();
container.Register<ITheDependency, TheDependency>(
    Reuse.InCurrentScope                                                      // tried various scopes here
    // setup: Setup.With(allowDisposableTransient: true)
    );
container.WithWebApi(config);

// and dependant controller
public class ValuesController : ApiController
{
    private readonly ITheDependency dependency;
    private readonly IContainer container;

    public ValuesController(ITheDependency dependency, IContainer container)
    {
        this.dependency = dependency;
        this.container = container;
    }

    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    public string Get(int id)
    {
        using (var scope = container.OpenScope())
        {
            var disposable = scope.Resolve<ITheDependency>();
        }

        return "value";
    }
}

New instance of TheDependency gets created and injected into ValuesController each time controller is necessary, however, once request is finished Dispose is never called on TheDependency. If scope is created and disposed manually (as in Get action with parameter) and TheDependency get resolved using that scope, Dispose is invoked.

Shouldn't disposable dependencies be disposed when web request scope is being disposed? What am I missing? DryIoc.WebApi.dll v5.0.0 is used.

dadhi commented 8 months ago

@Madajevas

Hi, I did not look at WebAPI for a long time and I don't remember who is responsible for disposing the scope.

From the code alone, I see that the framework should supposedly call the IDependencyScope scope = DryIocDependencyResolver.BeginScope().

And at end of request the scope should be disposed via scope.Dispose().

So, in principle, those methods should be hooked and called somewhere. I don't know where.

Madajevas commented 8 months ago

Yup. My debugging led me to the same conclusion: there is a resolver adapter (DryIocDependencyResolver) which, when framework calls BeginScope returns scope adapter (DryIocDependencyScope). When request is done processing Dispose method is indeed called on adapter and delegated to scope created. Anyway, in reality Dispose method in dependency is not invoked. Any suggestions how I could get to the bottom of this?

dadhi commented 8 months ago

Maybe try to search for DryIoc WebApi examples.

Madajevas commented 8 months ago

Got a bit further. If instance of HttpContextScopeContext is passed to WithWebApi registration, code gets to scope disposal, however disposables are recognized as ImHashMapEntry and .Disposed is called on controller, not service it depends on: image

dadhi commented 8 months ago

@Madajevas HttpContextScopeContext is available through the DryIoc.Web extension. It is separate from the DryIoc.WebApi. But you may use them together.

ImHashMapEntry

It is just an implementation detail which stores the disposables.