Reputation: 9255
I'm using Autofac with ASP.NET Core.
My dependency is a Reporter
:
public class Reporter {
public Reporter (bool doLogging) { DoLogging = doLogging ; }
public string DoLogging { get; set; }
// other stuff
}
I need to use it like this:
public class Foo
{
public Foo(Func<bool, Reporter> reporterFactory) { _reporterFactory = reporterFactory; }
private readonly Func<bool, Reporter> _reporterFactory;
}
And I want it to resolve like this:
_reporterFactory(false) ---> equivalent to ---> new Reporter(false)
_reporterFactory(true) ---> equivalent to ---> new Reporter(true)
I want the same instance per request (i.e. Autofac's InstancePerLifetimeScope
), for the same bool
parameter. When I call _reporterFactory(false)
multiple times, I want the same instance. And when I call _reporterFactory(true)
multiple times, I want the same instance. But those two instances must be different to each other.
So I register it like this:
builder
.Register<Reporter>((c, p) => p.TypedAs<bool>() ? new Reporter(true): new Person(false))
.As<Reporter>()
.InstancePerLifetimeScope(); // gives "per HTTP request", which is what I need
However, when I resolve I get the same instances regardless of the bool
argument:
var reporter = _reporterFactory(false);
var reporterWithLogging = _reporterFactory(true);
Assert.That(reporter, Is.Not.SameAs(reporterWithLogging)); // FAIL!
The documentation for "Parameterized Instantiation" says
resolve the object more than once, you will get the same object instance every time regardless of the different parameters you pass in. Just passing different parameters will not break the respect for the lifetime scope.
Which explains the behavior. So how do I register it correctly?
Upvotes: 2
Views: 993
Reputation: 3329
As mentioned in comments, you could use keyed services to achieve your goal:
builder.Register(c => new Reporter(true)).Keyed<IReporter>(true).InstancePerLifetimeScope();
builder.Register(c => new Reporter(false)).Keyed<IReporter>(false).InstancePerLifetimeScope();
The thing is, if you want to inject it to another class, you would have to inject it with IIndex<bool, IReporter>
:
public class Foo
{
public Foo(IIndex<bool, IReporter> reporters)
{
var withLogging = reporters[true];
var withoutLogging = reporters[false];
}
}
IIndex
is Autofac's interface, which makes your component tight coupled with the container, and this may not be desirable. To avoid this, you could additionally register the factory, like this:
builder.Register<Func<bool, IReporter>>((c,p) => withLogging => c.ResolveKeyed<IReporter>(withLogging)).InstancePerLifetimeScope();
public class Foo
{
public Foo(Func<bool, IReporter> reporters)
{
var withLogging = reporters(true);
var withoutLogging = reporters(false);
}
}
Now you have the working solution without coupling to the container itself.
Upvotes: 3