Именованный экземпляр Autofac - определите экземпляр по умолчанию, если не найден

Я использую Autofac для регистрации именованных экземпляров. Я должен переводить транзакции XML в объекты.

Во-первых, у меня есть перечисление.

public enum TransactionType
{
    Unknown = 0,
    [XmlNode("MyNodeA")]
    TypeA = 1,
    [XmlNode("MyNodeA")]
    TypeB = 2
}

У меня есть метод, который создает IDictionary<string, TransactionType> с использованием XmlNode атрибут на перечислении.

Вот мой картограф

var mappings = TransactionTypeHelper.GetDictionary();

foreach (var mapping in mappings)
{
    builder.Register(ctx => {
                return mapping.Key;
    })
    .Named<TransactionType>(mapping.Value)
    .InstancePerLifetimeScope();
}

Тогда у меня есть TransactionTypeFactory для получения TransactionType на основе узла XML.

public TransactionType GetTransactionType(string rootNode)
{
    return _container.Resolve<TransactionType>(rootNode?.ToLower());
}

Моя проблема в том, что я хочу пройти через любые неизвестные узлы xml как неизвестные транзакции, чтобы я мог обрабатывать новые транзакции без внесения каких-либо изменений в код. Проблема в том, что _container.Resolve выдает ошибку, если переданный узел не был зарегистрирован.

Я хочу, чтобы autofac возвращал enum по умолчанию, если именованный экземпляр не найден, вместо того, чтобы выдавать ошибку. Самое смешное, что у меня есть модульные тесты, в которых этот контейнер проверяется, и все они проходят, но Autofac специально взрывается при этом вызове.

0 ответов

Я знаю, что этот вопрос довольно старый, но я хотел бы поделиться решением, которое я узнал тем временем, в надежде, что он поможет кому-то с той же проблемой.

С помощью 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);
    }
}
Другие вопросы по тегам