2

I am misunderstanding the Autofac documentation on passing parameters to a Resolve method. The following is a minimalistic example of what I mean:

Example

https://dotnetfiddle.net/fz5eTp

public static void Main()
{
  var cb = new ContainerBuilder();
  cb.RegisterType<A>();
  cb.Register<B>((c, p) => new B(p.TypedAs<C>()));
  using (var c = cb.Build())
  {
    // works
    c.Resolve<B>(TypedParameter.From(new C()));
    // fails
    c.Resolve<A>(TypedParameter.From(new C()));
  }
}
public class A { public A(B b) { } }
public class B { public B(C c) { } }
public class C { }

Expected

I expected the TypedParameter to get passed down to B class.

Unfortunately the Exception message "Sequence contains no elements" suggests that no parameter was passed down.

Question

How can I pass parameters (only known at Resolve time) down to lower level constructors? Or in this specific example, how do I pass a C parameter down to B, when resolving a A.

Thierry Prost
  • 1,005
  • 11
  • 22
  • 2
    [This is an FAQ in the documentation.](https://autofac.readthedocs.io/en/latest/faq/injecting-global-parameters.html) – Travis Illig May 01 '19 at 12:01
  • 1
    Make sure your question stands on its own. Please make all relevant code snippets and exception details part of the question. – Steven May 01 '19 at 12:59
  • @TravisIllig. "figure out how you determine the parameter at runtime and wrap that in a provider or a lamda expression registration" --> Could you point me in the direction of how you would implement for an object which is provided by a different json each time at runtime? – Thierry Prost May 02 '19 at 06:30
  • I don't understand what you're asking - "a different json" from where? The "where" is important. This is the info you need to include in the question to get a sufficient answer. – Travis Illig May 02 '19 at 12:21

1 Answers1

1

Option1

The simplest way I can think of is to divide the resolving process into two steps:

First you can resolve the B by the specified known C value on run time, store it in a myB reference value:

B myB = c.Resolve<B>(TypedParameter.From(new C()));

And then pass myB value to the A resolver:

A myA = c.Resolve<A>(TypedParameter.From(myB));

You can find a full working example in my GitHub (with output).

Option2

If you like to write a one line of code on resolve time you will need to modify the register of A and B with:

cb.Register<A>((c, p) =>
{
    B myB = c.Resolve<B>(p);
    return new A(myB);
});
cb.Register<B>((c, p) => {
    C myC = p.Named<C>("myC");
    return new B(myC);
});

And then:

A myA = c.Resolve<A>(new NamedParameter("myC", new C()));

Again, you can find a full working example in my GitHub (with output).

Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91
  • These are very useful techniques which clearly help me understand Autofac a bit better. However they defy the very idea of IoC, don't they? – Thierry Prost May 02 '19 at 05:16
  • 1
    @ThierryProst, can you explain why? – Shahar Shokrani May 02 '19 at 05:57
  • I am by no means an expert on this, so I'll use the words of the autofac doc which Illig pointed to: "Passing C as a parameter to the A breaks the decoupling that interface-based development and inversion of control gives you by assuming that you “know” how the entire dependency chain is being resolved." While your approach is technically very correct and actually pretty ingenious, I guess that I have to think about the issue in a different way entirely... – Thierry Prost May 02 '19 at 06:10
  • 1
    Thats true, its a design issue, I'll update my answer – Shahar Shokrani May 02 '19 at 06:22