0

I'm using ASP.NET Core. I'm new to Autofac.

A third-party library (NLog) uses my Foo object for the app's lifetime, so it effectively becomes a singleton, and I can't change that. The problem is Foo has a scoped dependency, which effectively becomes a singleton too (a captive dependency).

In a related question I was told to change the dependency to Func<Dependency> so a new instance is created on each request. The builtin container doesn't support Func<>, so I'm using Autofac.

My type:

public class Foo
{
    public Foo(Func<IMyContext> contextFactory) { _contextFactory = contextFactory; }
    private readonly Func<IMyContext> _contextFactory;

    public void doSomething() { /* shown below */ }
}

My Startup:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyContext, MyContext>();
    services.AddSingleton<Foo>();
    var builder = new ContainerBuilder();
    builder.Populate(services);
    return new AutofacServiceProvider(builder.Build());
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory log)
{
    var foo = app.ApplicationServices.GetService<Foo>();
    ConfigureNLog(foo);    // effectively becomes a singleton, and I can't change that
    // ...
}

To test this during a request, I log the instance's hashcode:

public class Foo
{
    // ...

    public void doSomething()
    {
        using (var context = _contextFactory())
        {
            _logger.log(context.GetHashCode());   // should be unique on each request
        }
    }
}

I expect a unique hashcode on each request, because the dependency is created via a Func<> factory. But they are always the same! So it's the same instance on each request.

How can I solve this?

Community
  • 1
  • 1
grokky
  • 8,537
  • 20
  • 62
  • 96
  • 2
    Works for me: https://dotnetfiddle.net/YsGywP – CodeCaster Mar 12 '17 at 09:01
  • @CodeCaster that sample doesn't share anything remotely close to what the OP describes. There's no lifetime scopes, every service is registered as transient, and there's no capture of a single instance of the service that is supposed to create the context. – Mickaël Derriey Mar 12 '17 at 21:57
  • @CodeCaster Your code snippet helped a lot. Could you add as answer so I can accept? Also thanks for introducing me to dotnetfiddle which is really cool. – grokky Mar 14 '17 at 07:17
  • 1
    Well I think @Mickaël has a point, this question doesn't have much relation to your previous one, so I can see where he's coming from with his comment - I knew the background of the question, he didn't. Feel free to accept his answer. :) – CodeCaster Mar 14 '17 at 07:50
  • @CodeCaster Your code works, except that I needed to change your ctor injected arg from `Func` to `Func>`. Now it works exactly as intended. I don't understand why, but with that small mod, your code works great. – grokky Mar 14 '17 at 10:14

1 Answers1

1

TL;DR;

Change the registration of context so it looks like:

services.AddTransient<IMyContext, MyContext>();

This will give you a new instance of MyContext every time Foo invokes the Func<IMyContext>. Every time is important here, as it means that if the Func is invoked 10 times during the same HTTP request, 10 different instances will be created.

Longer version

Maybe I read it the wrong way, but here's what I think you want to achieve: you want the Func<IMyContext> injected into Foo to resolve to the same instance of MyContext per HTTP request.

I think this is not possible, and it's not an Autofac-specific issue.

The reason is the Func<IMyContext> injected into Foo is tied to the root lifetime scope - because Foo itself is created from the root container. This means that neither Foo not Func<IMyContext> know anything about potential nested lifetime scopes that are created for each HTTP request.

The only solution available to you is then to create a new instance of MyContext every time it's being resolved by the container.

Does that suit your requirements?

Mickaël Derriey
  • 12,796
  • 1
  • 53
  • 57
  • Glad you got it working, but I think giving more info would be beneficial. How did you register `MyContext` in the Autofac container? I'm not too sold on the fact that registering everything in Autofac is why it's working. – Mickaël Derriey Mar 14 '17 at 08:44
  • No the reason it now works is because I changed the injected ctor arg from `Func` to `Func>`. – grokky Mar 14 '17 at 10:14
  • I have a hard time figuring why this would change the behavior. `Owned` only exists to instruct Autofac not to track the service because you take responsibility of disposing it yourself. I understand you might not want to dig deeper since you got it working, but updating the original question by adding you current registrations might help figuring out what happens. – Mickaël Derriey Mar 14 '17 at 10:23
  • The Autofac author [just confirmed this behavior](http://github.com/autofac/Autofac/issues/834#issuecomment-286414627). So `Func>` seems necessary in this case. It's a "signal" to the container to resolve in a certain way. – grokky Mar 14 '17 at 13:45