Как обрабатывать исключение конструктора при использовании 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 не может быть создан
Затем я хочу вернуть дружественное сообщение об ошибке
И регистрировать фактическое исключение
Является ли это проблемой с моим дизайном, или существует известный шаблон для преодоления или предотвращения этой проблемы?
Я искал вокруг и не мог найти никакой информации по этому типу проблемы.
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