我正在使用Autofac中的adapter support将多种类型转换为所需类型。我还想保留附加到适配器输入类型的键/名称/元数据,以便它们在适配器输出类型上具有相同的值 - 这是使用IIndex<,>
按名称解析实例所需的。
我无法弄清楚如何通过适配器函数传播键/名称/元数据,因为适配器函数在组件构造期间运行,并且元数据需要在构建容器时传播。
这是一个示例xunit测试,它失败了:
/// <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"));
}
}
语句lookerUpper.LookupByName("A")
失败并带有ComponentNotRegisteredException
,因为名称值"A"
不会通过适配器功能传播(适应A - > B)。如果前两行Asserts被注释掉,那么测试的其余部分将按预期工作。
答案 0 :(得分:1)
我通过使用Autofac元数据而不是Autofac键或名称找到了解决此问题的可行解决方案。对于RegisterAdapter<TFrom, TTo>(Func<TFrom,TTo>)
的来电,元数据会从IComponentRegistration
TFrom
传播到IComponentRegistration
TTo
;但是密钥/名称不会传播。遗漏密钥可能是一个错误或设计,我将用autofac提交一个错误来弄清楚是哪种情况并跟进。
关于使用元数据的不幸部分是我不能使用IIndex<string, B>
构造函数参数,因此我必须使用IEnumerable<Meta<Lazy<B>>>
参数并创建我自己的字符串字典 - &gt; Lazy<B>
为IIndex
提供类似的功能。这是有效的代码:
/// <summary>
/// Unit test to figure out how to propagate keys through adapters.
/// </summary>
public sealed class AutofacAdapterTest
{
internal const string LookupKey = "lookup";
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 IDictionary<string, Lazy<B>> _bLookup;
public LookerUpper(IEnumerable<Meta<Lazy<B>>> bMetas)
{
_bLookup = bMetas.ToDictionary(meta => meta.Metadata[LookupKey].ToString(), meta => meta.Value);
}
public B LookupByName(string name)
{
return _bLookup[name].Value;
}
}
[Fact]
public void TestPropagateKeysThroughAdapters()
{
var builder = new ContainerBuilder();
// Register types that will be looked up; attach metadata for the lookup key
builder.Register((c) => new A("A")).WithMetadata(LookupKey, "A");
builder.Register((c) => new B("B")).WithMetadata(LookupKey, "B");
builder.Register((c) => new C("C")).AsSelf().As<B>().WithMetadata(LookupKey, "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<KeyNotFoundException>(() => lookerUpper.LookupByName("D"));
}
}
还应该可以创建IRegistrationSource
和一些扩展方法来扩展RegisterAdapter<TFrom, TTo>
中的内容,以便TFrom
中的密钥传播到TTo
- 这将是一个理想的解决方案,但可能需要维护更多的工作,所以我可能会坚持这一点。
答案 1 :(得分:1)
它已在Autofac版本3.5.1中修复。