Skip to content

Nested ContainerBuilder created with BeginLifetimeScope shares BuildCallback list with its parent ContainerBuilder #912

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

Closed
evil-shrike opened this issue Apr 27, 2018 · 5 comments
Labels

Comments

@evil-shrike
Copy link

var builder = new ContainerBuilder();
builder.RegisterModule(new MyModule ());
var container = builder.Build(); // here I'm getting InvalidOperationException

Inside MyModule I create a nested scope and register a BuildCallback for the nested container builder:

    public class MyModule : Module
    {
        protected override void Load(ContainerBuilder containerBuilder)
        {
            containerBuilder.RegisterBuildCallback(container =>
            {
                m_appScope = container.BeginLifetimeScope(nestedBuilder =>
                {
                    nestedBuilder.RegisterBuildCallback((c) => {
                      //
                    });
                });
                m_app = m_appScope.Resolve<IMyService>();
            });
        }
  }

But it seems that these two builders (the outer and the nested) share the same callbacks list. As I'm gettting an exception "Collection was modified; enumeration operation may not execute" on Build() call for the outer builder:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
Unhandled Exception:   at System.ThrowHelper.ThrowInvalidOperationException_InvalidOpera tion_EnumFailedVersion()
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at Autofac.ContainerBuilder.Build(ContainerBuildOptions options)

Autofac 2.8.0

@tillig
Copy link
Member

tillig commented Apr 27, 2018

This seems... complicated. What's the problem you're trying to solve by doing this nested series of callbacks?

@evil-shrike
Copy link
Author

The outer builder comes from asp.net app. The module wants to build a complex component. For this it needs some services from user code that have to be resolved. But we can't resolve them in ContainerBuilder. So we have to create a new LifetimeScope. But ContainerBuilder doesn't have BeginLifetimeScope so we're postponing that via RegisterBuildCallback.
Now inside BeginLifetimeScope's callback we have a nested ContainerBuilder. So the only question is why do I need another RegisterBuildCallback inside nested ContainerBuilder, right?

In real case code looks a bit more complex, I just simplified it :) So actually instead of that nested RegisterBuildCallback I'm using some builder/factory stuff to build my component.
Like:

        containerBuilder.RegisterBuildCallback(container =>
        {
            container.TryResolve(out IXApplicationFactory appFactory);
            m_appScope = container.BeginLifetimeScope(nestedBuilder =>
            {
                var appBuilder = new XApplicationBuilder(nestedBuilder);
                appFactory.Build(appBuilder);
            });
        }

That XApplicationBuilder doesn't know whether supplied ContainerBuidler is nested or not. It's just a ContainerBuilder for him and he wants to know when that ContainerBuilder is built.
I can work around the issue by abandoning using of the nested RegisterBuildCallback. But it looked as a bug for me.

@tillig
Copy link
Member

tillig commented Apr 27, 2018

We can definitely look into this but from a general standpoint I'd recommend separating your app startup code - the creation of an application level scope and the execution of your app factory stuff - from the population and building of the container. I can see how it might be easy to try tying it all together but I can also see high potential for getting into challenging and complicated situations like this.

Like I said, though, we will look into it. Thanks for the report!

@alistairjevans
Copy link
Member

This appears to have been fixed by the changes in the #1054 PR, as part of fixing #985.

Separate PR incoming with a proving test from the originally reported behaviour.

@tillig
Copy link
Member

tillig commented Dec 16, 2019

PR accepted so we won't regress this.

@tillig tillig closed this as completed Dec 16, 2019
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