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);
    }
}