I'm using the adapter support in Autofac to convert multiple types to a desired type. I also want to preserve the keys/names/metadata attached to the adapter input types, so that they exist with the same values on the adapter output types - this is needed for using IIndex<,>
to resolve instances by name.
I can't figure out how to propagate the keys/names/metadata through the adapter function, since the adapter function runs during component construction, and the metadata needs to be propagated when the container is built.
Here's an example xunit test, which fails:
/// <summary>
/// Unit test to figure out how to propagate keys through adapters.
/// </summary>
public sealed class AutofacAdapterTest
{
public class A
{
public A(string key)
{
Key = key;
}
public string Key { get; private set; }
}
public class B
{
public B(string name)
{
Name = name;
}
public string Name { get; private set; }
}
public class C : B
{
public C(string name)
: base(name)
{}
}
public class LookerUpper
{
private readonly IIndex<string, B> _bIndex;
public LookerUpper(IIndex<string, B> bIndex)
{
_bIndex = bIndex;
}
public B LookupByName(string name)
{
return _bIndex[name];
}
}
[Fact]
public void TestPropagateKeysThroughAdapters()
{
var builder = new ContainerBuilder();
// Register named types
builder.RegisterType<A>().Named<A>("A").WithParameter("key", "A");
builder.RegisterType<B>().Named<B>("B").WithParameter("name", "B");
builder.RegisterType<C>().Named<C>("C").Named<B>("C").WithParameter("name", "C");
// Adapter to convert an A to a B, since it's not a subclass
builder.RegisterAdapter<A, B>((c, a) => new B(a.Key));
// Register LookerUpper, which is the only top-level type that needs to be autowired
builder.RegisterType<LookerUpper>();
var container = builder.Build();
var lookerUpper = container.Resolve<LookerUpper>();
// Test expected results
Assert.Equal("A", lookerUpper.LookupByName("A").Name);
Assert.IsType<B>(lookerUpper.LookupByName("A")); // A should have been adapted to a B
Assert.Equal("B", lookerUpper.LookupByName("B").Name);
Assert.IsType<B>(lookerUpper.LookupByName("B"));
Assert.Equal("C", lookerUpper.LookupByName("C").Name);
Assert.IsType<C>(lookerUpper.LookupByName("C"));
Assert.Throws<ComponentNotRegisteredException>(() => lookerUpper.LookupByName("D"));
}
}
The statement lookerUpper.LookupByName("A")
fails with a ComponentNotRegisteredException
, because the name value "A"
is not propagated through the adapter function (which adapts A -> B ). If the first two lines of Asserts are commented out, the rest of the test works as expected.