Исключения и связь
У меня есть этот основной класс, который получает сообщение очереди, а затем использует несколько других классов, чтобы сделать некоторую работу. Все эти другие классы сами используют некоторые низшие классы, и в конечном итоге данные записываются в базу данных или отправляются в службы wcf.
Основываясь на результатах более низких классов, основной класс должен решить, следует ли удалить сообщение очереди, или снова поместить его в очередь, или отправить его в очередь deadletter.
Если, например, база данных недоступна, сообщение очереди может быть помещено в очередь, чтобы повторить попытку позже. Но если служба wdcf возвращает, что она не принимает некоторые данные, сообщение должно быть отправлено в очередь deadletter.
У меня есть несколько способов реализовать этот сценарий:
- Бросайте исключения и обрабатывайте их только в основном классе.
- Бросайте исключения, но ловите их в каждом вызывающем классе. И отбросить новое исключение
- Возврат объектов результата, которые указывают на ошибку / состояние успеха
Вот мои идеи о сценариях:
Если один из низших классов выдает исключение, и основной класс должен его обработать, он соединяет основной класс с низшими классами. Если один из низших классов решит изменить исключение, мне придется изменить обработку исключений основного класса.
Нет хорошего способа сообщить старшим классам, какие исключения будут выброшены из вызываемого класса в C#.
Это то, что я предпочитаю. Каждый вызванный метод может возвращать объект-результат, с перечислением, указывающим успех или неудачу, и тип ошибки.
Таким образом, я предпочитаю вариант 3, но я не знаю, приемлемо ли это архитектурно. Или, если есть какие-то лучшие способы.
Код
Вот как выглядит код (в упрощенном виде):
QueueHandler
private static void HandleQueueMessage(Message message)
{
var deliveryOrder = deserialize(message.body);
var deliveryOrderHandler = new DeliveryOrderHandler();
var result = deliveryOrderHandler.Execute(deliveryOrder.PubId);
switch (result)
{
case DeliveryOrderHandlerResult.DeliverySucceeded:
break;
case DeliveryOrderHandlerResult.FataleErrorInExternalSystem:
case DeliveryOrderHandlerResult.MAndatoryDocuhmentTransformationFailed:
SendDeliveryOrderToDeadletterQueue(deliveryOrder);
break;
default:
deliveryOrder.AbortCount = deliveryOrder.AbortCount + 1;
ResendDeliveryOrderToQueue(deliveryOrder);
break;
}
}
DeliveryOrderHandler
private DeliveryOrderHandlerResult Execute(long pubId)
{
DeliveryOrderHandlerResult deliveryOrderHandlerResult;
var transformationResult = GetTransformationResultaat(pubId);
if (transformationResult == TransformationResult.Success)
{
var deliveryResult = DeliverDocumentToExternalSystem(pubId);
if (deliveryResult.Status == DeliveryResult.Success)
{
SaveDeliveryResult(pubId, deliveryResult);
}
deliveryOrderHandlerResult = deliveryResult.Status;
}
else
{
switch (transformationResult)
{
case TransformationResult.NotStarted:
deliveryOrderHandlerResult = DeliveryOrderHandlerResult.TransformationNotStarted;
case TransformationResult.Busy:
deliveryOrderHandlerResult = DeliveryOrderHandlerResult.TransformationBusy;
case TransformationResult.MandatoryTransformationFailed:
deliveryOrderHandlerResult = DeliveryOrderHandlerResult.MandatoryTransformationFailed;
default:
throw new Exception(--unknown enum value --);
}
}
return deliveryOrderHandlerResult;
}
DeliverDocumentToExternalSystem
pseudo:
- Create Delivery package by reading data from database and transformed files from disk
- Send package to external system
Как видите, многое может пойти не так; сбой соединения с базой данных, сбой вызовов службы wcf, файлы отсутствуют и т. д.
Я надеялся, что смогу предотвратить это:
QueueHandler
private static void HandleQueueMessage(Message message)
{
var deliveryOrder = deserialize(message.body);
var deliveryOrderHandler = new DeliveryOrderHandler();
try
{
var result = deliveryOrderHandler.Execute(deliveryOrder.PubId);
switch(result)
{
case DeliveryOrderHandlerResult.Success:
// remove message from queue
case DeliveryOrderHandlerResult.NotStarted:
// resent message to queue
case DeliveryOrderHandlerResult.MandatoryTransformationFailed:
// send message to deadletterqueue
case ...
// handle
case ...
// handle
}
}
pseudo catches:
catch (DatabaseNotFoundexception ex)
{
// resent message to queue
}
catch (ExternalWcfServiceDownException ex)
{
// resent message to queue
}
catch (FileNotFoundException ex)
{
// send message to deadletterqueue
}
catch (...)
{
// handle
}
catch (...)
{
// handle
}
}
1 ответ
- ... он соединяет основной класс вплоть до низших классов.
Нет. Он соединяет ваш основной класс с типами исключений. Да, если вы измените исключения ниже, вам нужно будет изменить обработку выше, но связь находится на уровне исключения.
3 не очень хороший вариант, так как вы можете забыть проверить результаты. В конечном итоге вы сталкиваетесь с той же самой проблемой, которую вы перечислили в варианте 1, как будто вы изменяете объекты результата вниз, вам нужно внести изменения в ваш основной класс...
Исключения считаются лучшим вариантом, так как вам не нужно проверять, не произошла ли ошибка.