Reputation: 657
According to the Parameterized Instantiation (Func<X, Y, B>
) documentation:
However, if you register an object as SingleInstance() and call the Func<X, Y, B> to 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.
How does a singleton class with different parameter constructor work?
Upvotes: 3
Views: 988
Reputation: 16192
Autofac factory registration won't cache instances per parameters.
Let's try with the following type:
public class Foo
{
public Foo(Int32 a)
{
this._a = a;
}
private readonly Int32 _a;
public override String ToString()
{
return String.Format("a={0}", this._a);
}
}
If you register it like this:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().AsSelf().SingleInstance();
Autofac will allways register a single instance, no matter how you resolve it (even if you use a Func
factory):
Func<Int32, Foo> foo1Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo1 = foo1Factory(1);
Func<Int32, Foo> foo2Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo2 = foo2Factory(2);
foo1
and foo2
will be the same instance.
If you want to have only one instance per parameter, this implies that you want more than a single instance of Foo
, so you can't register Foo
using the SingleInstance()
method. You will have to implement a custom factory that will do caching to have one instance per parameter:
public class FooFactory
{
public FooFactory(IComponentContext componentContext)
{
this._componentContext = componentContext;
this._instances = new Dictionary<Int32, Foo>();
this._lock = new Object();
}
private readonly IComponentContext _componentContext;
private readonly Dictionary<Int32, Foo> _instances;
private readonly Object _lock;
public Foo GetInstance(Int32 a)
{
Foo parameterizedFoo;
if (!this._instances.TryGetValue(a, out parameterizedFoo))
{
lock (this._lock)
{
if (!this._instances.TryGetValue(a, out parameterizedFoo))
{
Parameter aParameter = new TypedParameter(typeof(Int32), a);
parameterizedFoo = this._componentContext
.Resolve<Foo>(aParameter);
this._instances[a] = parameterizedFoo;
}
}
}
return parameterizedFoo;
}
}
You can register it this way:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().AsSelf();
builder.RegisterType<FooFactory>().AsSelf().SingleInstance();
Use the following code to use it:
FooFactory fooFactory = scope.Resolve<FooFactory>();
Foo foo = fooFactory.GetInstance(1);
But if you want to use Func<Int32, Foo>
instead of FooFactory.GetInstance
, you can register the method as Func<Int32, Foo>
:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().AsSelf();
builder.RegisterType<FooFactory>().AsSelf().SingleInstance();
builder.Register(c => new Func<Int32, Foo>(c.Resolve<FooFactory>().GetInstance))
.As<Func<Int32, Foo>>()
.SingleInstance();
You now can use this code:
Func<Int32, Foo> foo1Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo1 = foo1Factory(1);
Func<Int32, Foo> foo2Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo2 = foo2Factory(2);
Func<Int32, Foo> foo1PrimeFactory = scope.Resolve<Func<Int32, Foo>>();
Foo foo1Prime = foo1PrimeFactory(1);
foo1
and foo2
will be different whereas foo1Prime
will be same as foo1
.
Upvotes: 7
Reputation: 657
finally, my workmate has a similar approach
public interface IKey
{
}
public interface IKeyed<out T>
{
T this[IKey key] { get; }
}
public class Keyed<T> : IKeyed<T>
{
private ConcurrentDictionary<IKey, T> _dict = new ConcurrentDictionary<IKey,T>();
T IKeyed<T>.this[IKey key]
{
get
{
return _dict.GetOrAdd(key, k => IoC.Resolve<T>(new TypedParameter(key.GetType(), key)));
}
}
}
and in app start, register
_builder.RegisterGeneric(typeof(Keyed<>))
.As(typeof(IKeyed<>))
.SingleInstance();
Upvotes: 0