autofac/Autofac.Extensions.DependencyInjection

Enable opt-in ability to create all lifetime scopes from the root scope instead of hierarchical

garv347 opened this issue · 9 comments

Problem Statement

I was using a library that uses the .net core DI and my application uses the Autofac DI. There is a behavior difference in how Autofac creates new scope vs how .net core creates new scope. Autofac creates a new scope following the tree kind of structure whereas .net core DI creates a new scope from the same base level making it flattened (2 level tree)in nature. To overcome this, I had to create a local AutofacServiceScopeFactory class(copied from Autofac.Extensions.DependencyInjection) and use the following line to replace IServiceScopeFactory in the .net core.

"services.AddSingleton<IServiceScopeFactory, AutofacServiceScopeFactory>();"

Desired Solution

Can we add a switch where we can make Autofac work with .net core DI smoothly?

From the description it's not clear what the issue is. That is, I understand the difference between hierarchical and flat scopes, but I don't understand why that's a problem. Can you get a little deeper, maybe show some code, and help us understand what your use case is and why you think what's happening is wrong?

Autofac implements the interface and passed all of the behavioral compatibility tests with the .NET Core default DI, That said, when you pick a different backing container - whether it's Autofac, StructureMap, Simple Injector, or any other - there are going to be some behind the scenes differences. Generally if you pick a different container it's because you want that.

Which is a long way of saying... We likely won't change how lifetime scopes work. But if you can explain the problem, perhaps we can see if there's some edge case we're mishandling or possibly suggest an alternative.

I want IServiceScopeFactory to create a new sibling scope rather than child scope.

Again, as noted, I understand what you want, but not why. In general if you want a DI framework to behave like a particular DI framework, you'd just... use the other DI framework. Part of the benefit of choosing Autofac as the backing container is that it uses this tree structure; that if you want to do "unit of work" sorts of things in the controller, you can, because you'll get the hierarchical set of scopes you know and love from Autofac.

In the pull request, what I see is the desire to change that, such that all scopes effectively come off the root scope... which is very much both:

  • not what folks using Autofac generally want; and
  • an opt-in feature that we'd have to own, maintain, and support for what appears to be a single-person use case at the moment

Hence, it's really important to understand the use case - why you need or what this flat scope mechanism. It may be that there's a way to solve your challenge in a different manner, or it may be that we need to leave this open for a while to see if other folks are also interested in the same functionality before committing to owning and maintaining it. If no one else finds it interesting, it may be that you will need to own your own fork of the library to do the special one-off behavior.

But before any of that gets decided, we need to understand more about the reason behind this.

I don't always need hierarchical.

In my business code, asp.net core use Microsoft.Extensions.DependencyInjection, asp.net, wcf use Autofac bridge to Microsoft.Extensions.DependencyInjection.
In my sdk, I use Microsoft.Extensions.DependencyInjection, if CreateScope behavior is different, I might get an exception.

var subScope = serviceProvider.CreateScope();

var subScope2 = subScope.ServiceProvider.CreateScope();

subScope.Dispose();

subScope2.ServiceProvider.GetRequiredService<XXX>(); //This line may be throw ObjectDisposedException.

What I'm hearing is that in your code you create lifetime scopes on your own and you're not tracking the order of creation/disposal, which results in occasional errors where you dispose a parent scope out from under a child scope. Is that correct?

What I'm hearing is that in your code you create lifetime scopes on your own and you're not tracking the order of creation/disposal, which results in occasional errors where you dispose a parent scope out from under a child scope. Is that correct?

If I don't use Autofac, that code no problem.

      ///.UseServiceProviderFactory(new AutofacServiceProviderFactory())

And, I just want to create an unrelated scope(except root scope).
I don't think AutofacServiceProviderFactory behavior has any wrong, but we want we have some ability let the behavior as like as Microsoft.Extensions.DependencyInjection.

I don't think the assumed behavior of non-hierarchical is a safe assumption. It's like assuming that every IEnumerable is always the same order instead of using IOrderedEnumerable.

Which is to say, if you require a scope off the root you should hold a reference to the root and explicitly spawn from that rather than changing the behavior of the underlying DI system based on context.

I'm happy to leave this open for a while to see if other folks are interested. If so, we can consider accepting the pull request. Otherwise, right now I'd recommend either holding the root provider in your code (eg as a static) or maintain a fork of this repo yourself with the change.

This has been open for a few months and has no thumbs-up votes or comments from folks looking for similar functionality. I stand by the notion that scopes in Autofac are hierarchical and we're not really interested in trying to bypass that. This concept seems to work fine for lots of people in lots of apps, so supporting a feature like this for a limited use case is not really something we can take on at the moment.

It appears it's possible to write this as an extension or as a standalone package, so if you're interested in having this feature, at this point I'd recommend creating a package of your own and maintaining it.

ohroy commented

More discussion here #83