Autofac: 4.0.0-rc3-309 - updating registry

Created on 13 Jul 2016  路  8Comments  路  Source: autofac/Autofac

Version : 4.0.0-rc3-309

Problem: Updating the registry fails to register the component in the right scope. The attached test case works as expected on version 4.0.0-rc3-293

Test Case:

public class Program
{
    public static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<WorkerContextRegistrar>().As<IWorkerContextRegistrar>();
        builder.RegisterType<PrintWorkId>().As<IWorker>();
        var container = builder.Build();

        while (true)
        {
            using (var scope = container.BeginLifetimeScope())
            {
                // register worker context
                var workId = Guid.NewGuid();
                var requestContextRegistrar = scope.Resolve<IWorkerContextRegistrar>();
                requestContextRegistrar.RegisterContext(new WorkerContext(workId));

                // do work
                var worker = scope.Resolve<IWorker>();
                worker.DoWork();

                Thread.Sleep(1000);
            }
        }
    }
}

internal interface IWorker
{
    void DoWork();
}

internal class PrintWorkId : IWorker
{
    private readonly IWorkerContext _context;

    public PrintWorkId(IWorkerContext context)
    {
        _context = context;
    }

    public void DoWork()
    {
        Console.WriteLine("work - " + _context.Id);
    }
}

internal interface IWorkerContextRegistrar
{
    void RegisterContext(IWorkerContext context);
}

internal interface IWorkerContext
{
    Guid Id { get; }
}

internal class WorkerContext : IWorkerContext
{
    public WorkerContext(Guid id)
    {
        Id = id;
    }
    public Guid Id { get; }
}

internal class WorkerContextRegistrar : IWorkerContextRegistrar
{
    private readonly IComponentContext _registerContext;

    public WorkerContextRegistrar(IComponentContext registerContext)
    {
        _registerContext = registerContext;
    }

    public void RegisterContext(IWorkerContext context)
    {
        var builder = new ContainerBuilder();
        builder.RegisterInstance(context).As<IWorkerContext>();
        builder.Update(_registerContext.ComponentRegistry);
    }
}

Thanks,
Javier.

Most helpful comment

I pushed v4.0.0-rc3-316 to NuGet with the fixes on the MyGet feed. I actually started hitting this, too, in a project of mine just Friday morning, so nice timing on finding it.

All 8 comments

@alexmg I think this may be related to the activator caching thing added recently. That's the only thing I can think of and really all that changed between core Autofac releases.

@javier-troconis-q2 In general it's good to consider containers/component registries immutable wherever possible. Not that we won't try and fix it, just that whenever I see Update somewhere it sends up red flags for me. You can register things on the fly during BeginLifetimeScope() by passing in a lambda. You may be able to work around the issue (or come out with a better longer-term solution) by making use of that.

@tillig Thanks for your quick response. Making the registrations immutable would be ideal, the thing is that for our scenario, the scope creation happens before I have the contextual values I want to include with my component registration. The provided test case was meant to illustrate the issue in a simple way, however our situation is that we're running asp.net core and we don't have control over the scope creation to pass in any custom registrations. I believe it is best to pass any contextual information with the incoming message rather than register a component on the fly that gives me this data.

@tillig @alexmg , even using a lambda expression in the BeginLifetimeScope factory method yields cached values, here's a small snippet that proves it :

public class Program
{
       class A
       {
           public readonly Guid Id;

           public A(Guid id)
           {
               Id = id;
           }
       } 

       public static void Main(string[] args)
       {
            var builder = new ContainerBuilder();
            builder.RegisterType<A>().InstancePerLifetimeScope();
            var container = builder.Build();

            while (true)
            {
                using (var scope = container.BeginLifetimeScope(x => x.Register(z => Guid.NewGuid())))
                {
                    // these should resolved values should match, but they don't
                    var k = scope.Resolve<Guid>();
                    Console.WriteLine(k);
                    var x = scope.Resolve<A>();
                    Console.WriteLine(x.Id);
                    Thread.Sleep(1000);
                }
            }
      }
}

Awesome, thanks for these repros! This will help track down the issue much faster.

@tillig @alexmg Thanks for looking into this. We've been along for the ride since beta3 and know the pain you wrote about in your last blog post. It's been very interesting watching https://github.com/aspnet/DependencyInjection/pull/416#issuecomment-229669506. If there's something we can do to help on this issue beyond supplying the repro, let us know.

I don't think the last snippet was actually demonstrating the issue, as it didn't have InstancePerLifetimeScope applied to the Guid.NewGuid() delegate registration.

container.BeginLifetimeScope(x => x.Register(z => Guid.NewGuid()).InstancePerLifetimeScope())

I have created a unit test that reproduces the issue and confirmed that reverting the ConstructorParameterBinding caching addresses the issue. Those changes were going to be reverted anyway in favour of finding a better approach.

You can test the fix using the latest package on our MyGet feed.

https://www.myget.org/F/autofac/api/v3/index.json

It has been a painful journey but I think it will all be worthwhile in the end. :smile:

hey @alexmg, yep, forgot to add the InstancePerLifetimeScope policy to the scope registration. Thanks a lot for you help on this, it works now 馃憤

I pushed v4.0.0-rc3-316 to NuGet with the fixes on the MyGet feed. I actually started hitting this, too, in a project of mine just Friday morning, so nice timing on finding it.

Was this page helpful?
0 / 5 - 0 ratings