Autofac 命名实例 - 如果找不到则定义默认实例
Autofac Named Instance - Define Default Instance if Not Found
我正在使用 Autofac 注册命名实例。我必须将 xml 交易翻译成对象。
首先,我有一个枚举。
public enum TransactionType
{
Unknown = 0,
[XmlNode("MyNodeA")]
TypeA = 1,
[XmlNode("MyNodeA")]
TypeB = 2
}
我有一个使用枚举上的 XmlNode
属性创建 IDictionary<string, TransactionType>
的方法。
这是我的 autofac 映射
var mappings = TransactionTypeHelper.GetDictionary();
foreach (var mapping in mappings)
{
builder.Register(ctx => {
return mapping.Key;
})
.Named<TransactionType>(mapping.Value)
.InstancePerLifetimeScope();
}
然后,我有一个 TransactionTypeFactory
用于根据 xml 节点获取 TransactionType
。
public TransactionType GetTransactionType(string rootNode)
{
return _container.Resolve<TransactionType>(rootNode?.ToLower());
}
我的问题是我想通过任何未知的 xml 节点作为未知交易,这样我就可以在不更改任何代码的情况下处理新交易。问题是如果传入的节点尚未注册,_container.Resolve
会抛出错误。
我想做的是在找不到命名实例时将 autofac return 设置为枚举默认值,而不是抛出错误。有趣的是,我在模拟这个容器的地方进行了单元测试,它们都通过了,但是 Autofac 在这个调用中特别失败。
我知道这个问题比较老,但我想分享一下我在此期间学到的解决方案,希望它能对遇到同样问题的人有所帮助。
使用 autofac,您可以注册一个可以使用逻辑解析的函数。
首先,您将注册每个命名实例。在这个问题中,我用一个助手来做这个并遍历一个集合,但本质是将枚举的每个值映射到一个实例。
builder.Register<TransactionAClass>(ctx =>
{
//get any instances required by ConcreteClass from the ctx here and pass into the constructor
return new TransactionAClass();
})
.Named<Interfaces.ITransactionInterface>($"{TransactionType.TypeA:f}")
.InstancePerLifetimeScope();
一旦您注册了所有已知值,我们就会注册一个解析器函数。
builder.Register<Func<TransactionType, Interfaces.ITransactionInterface>>(ctx =>
{
//you must resolve the context this way before being able to resolve other types
var context = ctx.Resolve<IComponentContext>();
//get the registered named instance
return (type) =>
{
var concrete = context.ResolveNamed<Interfaces.ITransactionInterface>($"{type:f}");
if (concrete == null)
{
//return a default class or throw an exception if a valid registration is not found
return new TransactionAClass();
}
return concrete;
}
});
然后,你可以像这样使用解析器
public class MyClass
{
private readonly ITransactionInterface transaction;
public MyClass(Func<TransactionType, Interfaces.ITransactionInterface> transactionResolver)
{
transaction = transactionResolver.Invoke(TransactionType.TypeA);
}
}