Как обрабатывать исключение конструктора при использовании Autofac WcfIntegration



Существует ли способ обработки исключения, создаваемого конструктором службы WCF, когда этот конструктор принимает зависимость, и именно создание экземпляра зависимости контейнером IoC (в данном случае AutoFac) вызывает исключение?



Рассмотрим службу WCF со следующим конструктором:



public InformationService(IInformationRetriever informationRetriever)
{
_informationRetriever = informationRetriever;
}
//... the service later makes use of the injected InformationRetriever


Служба использует AutoFac WcfIntegration и AutofacWebServiceHostFactory (это, оказывается, RESTful service).



Зависимости регистрируются в глобальный.асакс.cs службы, то есть:



builder.RegisterType<InformationRetriever>()
.As<IInformationRetriever>()


Теперь реализация InformationRetriever выполняет некоторые проверки в своем конструкторе, чтобы убедиться, что все готово для выполнения своей работы. Когда он обнаруживает проблему в этой фазе, он создает исключение.



Однако я не хочу, чтобы вызывающий службу получал исключение AutoFac:



An exception was thrown while invoking the constructor ... on type InformationRetriever



Эффективно я пытаюсь проверить:



Данный информационный сервис работает



Когда Я вызываю метод GetSomeInformation ()



И InformationRetriever не может быть создан



Затем я хочу вернуть дружественное сообщение об ошибке



И регистрировать фактическое исключение



Является ли это проблемой с моим дизайном, или существует известный шаблон для преодоления или предотвращения этой проблемы?



Я искал вокруг и не мог найти никакой информации по этому типу проблемы.
507   2  

2 ответов:

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

builder
    .Register(c => {
        try
        {
            return new InformationRetriever();
        }
        catch (Exception)
        {
            return new FailoverInformationRetreiver();
        }})
    .As<IInformationRetriever>();

... где FailoverInformationRetreiver создает исключения для доступа к членам. Другой идеей может быть сделать:

public InformationService(Lazy<IInformationRetriever> informationRetriever)
{
    _informationRetriever = informationRetriever;
}

И try/catch вокруг обычаев внутри InformationService. Другой вариант, который вы можете использовать, если доступность InformationRetriever известна при запуске приложения:

// inside your container builder:
if (InformationRetreiver.IsAvailable())
    builder.RegisterType<InformationRetriever>()
           .As<IInformationRetriever>()

// inside InformationService, make the dependency optional
public InformationService(IInformationRetriever informationRetriever = null)
{
    _informationRetriever = informationRetriever;
}

Сделайте что-нибудь из этого идеи помогают?

Объекты, написанные в стиле DI, обычно проходят через две отдельные фазы: композицию и исполнение. Фаза композиции - это когда вы подключаете зависимости и делаете такие вещи,как исключение аргументов. Как правило, вы хотите, чтобы эта фаза была свободна от осмысленного поведения, так как это позволяет выявлять ошибки в конфигурации вашей системы. Вторая фаза, выполнение, - это когда вы используете выходные данные первой фазы (зависимости) для выполнения своей работы.

Разделение эти две фазы устраняют много двусмысленности и сложности. Например, вы не пытаетесь косить газон во время заправки газонокосилки; это приводит к тому, что оба вида деятельности становятся более сложными (и опасными!)

В этом случае InformationRetriever объединяет фазы композиции и выполнения, выполняя осмысленную работу в своем конструкторе. Это смешивание вызывает именно ту проблему, которую вы пытаетесь избежать: значимое исключение бизнеса, обернутое в исключение композиции. Это тоже непонятно как обработать исключение, так как вызыватель верхнего уровня-Autofac, а не компонент, который фактически просит InformationRetriever выполнить работу.

Я предлагаю стремиться выполнять проверку при вызове InformationRetriever; это удаляет исключение Autofac и позволяет InformationService обрабатывать исключительную ситуацию без каких-либо уловок.

Одним из потенциальных недостатков этого подхода является то, что проверка будет происходить при каждом вызове InformationRetriever, а не один раз в конструкторе. У вас есть два варианта: 1) пусть это повторяйте каждый раз, чтобы быть абсолютно уверенным, что работа действительно выполняется, или 2) Следите за тем, выполнили ли вы проверку, и делайте это только в том случае, если вы этого не делали раньше.

Если вы выберете #2, вы можете сохранить InformationRetriever чистым, используя декоратор, чтобы обернуть его в проверяющую версию того же интерфейса:

public class ValidatingInformationRetriever : IInformationRetriever
{
    private readonly IInformationRetriever _baseRetriever;
    private bool _validated;

    public ValidatingInformationRetriever(IInformationRetriever baseRetriever)
    {
        _baseRetriever = baseRetriever;
    }

    public void Foo()
    {
        if(!_validated)
        {
            Validate();

            _validated = true;
        }

        _baseRetriever.Foo();
    }

    private void Validate()
    {
        // ...
    }
}

Вы можете зарегистрировать его с помощью поддержка декоратор Autofac по, например:

builder
    .RegisterType<InformationRetriever>()
    .Named<IInformationRetriever>("base");

builder.RegisterDecorator<IInformationRetriever>(
    (c, inner) => new ValidatingInformationRetriever(inner),
    fromKey: "base");

Comments

    Ничего не найдено.