views:

855

answers:

3

Can't get my head around the parameter passing in Autofac, the following code doesn't work:

class Config {
    public Config(IDictionary<string, string> conf) {}
}

class Consumer {
    public Consumer(Config config) {}
}

void Main()
{
    var builder = new Autofac.Builder.ContainerBuilder();
    builder.Register<Config>();
    builder.Register<Consumer>();
    using(var container = builder.Build()){
     IDictionary<string,string> parameters = new Dictionary<string,string>();
     var consumer = container.Resolve<Consumer>(Autofac.TypedParameter.From(parameters));
    }
}

that throws:

DependencyResolutionException: The component 'UserQuery+Config' has no resolvable constructors. Unsuitable constructors included:
Void .ctor(System.Collections.Generic.IDictionary`2[System.String,System.String]): parameter 'conf' of type 'System.Collections.Generic.IDictionary`2[System.String,System.String]' is not resolvable.

but the following code does work:

IDictionary<string,string> parameters = new Dictionary<string,string>();
var config = container.Resolve<Config>(Autofac.TypedParameter.From(parameters));
var consumer = container.Resolve<Consumer>(Autofac.TypedParameter.From(config));
A: 

Autofac is obviously trying to resolve the parameter of your Config class in the assumption that the Dictionary itself is a resolvable type. I do not know the autofac syntax on how to do it. But you probably need to do more steps when registring the Config type, e. g. giving it a delegate that passes in a new Dictionary.

galaktor
you're right! but then next problem arise, if i register Config as follows instead:builder.Register<Config>((c, p) => new Config(p.TypedAs<IDictionary<string, string>>()));p is empty at resolve time!
Carl Hörberg
Hm. Maybe that's because you give autofac a delegate, but then the delegate still does not have the Dictionary at runtime. Try actually providing a new Dictionary in your delegate, something like this (be aware, I still do not know exact forumulations for autofac):builder.Register((c) => new Config(new Dictionary<string,string>()));
galaktor
won't that always give Config a empty Dictionary? despite what arguments are passed at resolve time?
Carl Hörberg
Yes, it will. In your example code you do the same at resolve time, crate a new Dictionary and put it in the Config.
galaktor
haha, true ;) but the point is the ability to inject custom parameters.. the content of the Dictionary would only be known at runtime.
Carl Hörberg
Is this what you are doing?http://code.google.com/p/autofac/wiki/ResolveParameters
galaktor
+3  A: 

Repeating here the answer from the Autofac mailing list:

The parameters passed to Resolve only related to the direct implementer of the service you're resolving, so passing Config's parameters to the resolve call for Consumer won't work. The way around this is to change your Consumer registration to:

builder.Register((c, p) => new Consumer(c.Resolve<Config>(p)));

Nicholas Blumhardt
i guess you mean:builder.Register((c, p) => new Consumer(c.Resolve<Config>(p)));because that works perfetct! Thanks Nicholas!
Carl Hörberg
+1 finally someone who knows autofac :-)
galaktor
+1  A: 

Unfortunately, IoC containers like Autofac doesn't come equipped with a "please read my mind module".

What you're trying to do is basically saying "I know one of the types involved here needs a dictionary, and I need a service of type Consumer, can you please try to figure out what it is that I'm taking about and just do the right thing?".

If you resolve one service, and specify a parameter, that parameter will be attempted to be used for that particular service. The container will not try to propagate that parameter value down to any dependencies.

Lasse V. Karlsen
sure, my first attempt was naive, but not way out, if the parameters had been treaded as registrations it had work?
Carl Hörberg
Could be, AutoFac should then be able to figure it out, but you'd need to pre-register the parameters as services with AutoFac. Personally I don't recommend services with constructor parameters, it is invariably a leaky service since it leaks implementation details into code that uses it, makes it harder to later on replace the service. Consider as an example a service where you will pass a configuration filename to it, what then if you later on decide to use a service that reads the configuration from a database instead?
Lasse V. Karlsen
yes i know, my solution isn't spot on but the thing is, i want the user to be able to configure what type of Service to be used. But that service also need different configurations, so now these service configurations are passed in as IDictionary<string,string>.. i would love better solutions to it, but this factory thingy is my best attempt so far..
Carl Hörberg
Can't you simply create configuration providers as services, interfaces with a method in them that return that dictionary? That way you could easily unit-test the code (provide code-driven configuration) as well as wire up your code without having to specify any configuration at the call-site.
Lasse V. Karlsen