I'm trying some autofac features at the moment and are struggling with the NamedParameter.
I created a very simple example, where IFoo
is realized by HappyFoo
and SadFoo
. IFooUser
expects an IFoo
object in the constructor and uses it to print something to the console.
The code below shows how I'm trying to build a IFooUser
object which shall use an IFoo
object based on a 'score' value.
var builder = new ContainerBuilder();
builder.RegisterType<NormalFooUser>().As<IFooUser>();
builder.Register<IFoo>((context, parameters) =>
{
var score = parameters.Named<int>("score"); // ! Exception !
if (score >= 100)
{
return new HappyFoo();
}
return new SadFoo();
});
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
scope.Resolve<IFooUser>(new NamedParameter("score", 90)).useFoo();
}
Unfortunately when I execute this code, the parameters
sequence doesn't hold any values => Exception! When I resolve to IFoo
everything works fine:
scope.Resolve<IFoo>(new NamedParameter("score", 90)).foo(); // Works
But this is not what I want to do. So I'm wondering how I can construct a IFooUser
with an IFoo
object corresponding to the 'score' with the NamedParameter.
The complete code-sample can be found here as gist
Thanks.
NamedParameter
is intended to configure the requested component and not the whole dependency graph, the parameter is not propagated through the whole registration process it is not a context parameter.
If you want to have a context you can implement such a component which will store some properties.
// interface
interface IContext {
T Get<T>(String name);
void Set<T>(String name, T value);
}
// registration
builder.RegisterType<FooContext>().As<IContext>();
builder.Register<IFoo>(c =>
{
var score = c.Resolve<IContext>().Get<Int32>("score");
if (score >= 100)
{
return new HappyFoo();
}
return new SadFoo();
});
// usage
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<IContext>().Set("score", 90);
scope.Resolve<IFooUser>().useFoo();
}
The advantage of this approach is that you can set the context property whenever you want in your current lifetime scope. You can also imagine implementation without Set
method which will retrieve value based on whatever you want.
Another way of doing the same thing is by using a Func<Int32, IFoo>
dependency and the TypedAs
methods
builder.Register<IFoo>((c, p) =>
{
Int32 score = p.TypedAs<Int32>();
if (score >= 100)
{
return new HappyFoo();
}
return new SadFoo();
});
and of course you have to change the IFooUser
interface and implementation
public class NormalFooUser : IFooUser
{
private readonly Func<Int32, IFoo> _fooBuilder;
public NormalFooUser(Func<Int32,IFoo> fooBuilder)
{
_fooBuilder = fooBuilder;
}
public void useFoo(Int32 score)
{
_fooBuilder(score).foo();
}
}