Обработка исключений с помощью сценария WCF плюс уровень бизнес-логики
Мои сервисы просто вызывают методы BusinessLogicLayer, в которые помещается вся бизнес-логика. Я хочу знать, что является лучшим методом для обработки исключений, вызванных BL?(Не только фатальные исключения, но также и "логические" исключения ApplicationExotFoundException, которые выдает мой BL, когда не удается найти пользователя).
Где я должен преобразовать эти исключения в FaultExceptions, которые увидит клиент?
Должен ли я выбросить бизнес-исключения из BL, а затем перехватить их в вызове службы, преобразовать в FaultException и вернуть клиенту? или BL должен поднять уже "дружественные к клиенту" FaultExceptions?
заранее спасибо:)
2 ответа
Я бы сказал, выбросить бизнес-исключение из уровня бизнес-логики, это позволит отделить уровень бизнес-логики от реализации wcf. В сервисном вызове вы можете переопределить applydispatchbehaviour и добавить туда обработчик ошибок, что-то вроде
Переопределение IServiceBehavior.ApplyDispatchBehavior
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(new FaultErrorHandler());
}
}
FaultErrorHandler
public class FaultErrorHandler : IErrorHandler
{
void IErrorHandler.ProvideFault(System.Exception error, MessageVersion version, ref Message fault)
{
if (fault == null)
{
FaultException<[ExceptionType]> fe = new
FaultException<[ExceptionType]>([Exception cass],
error.Message, FaultCode.CreateReceiverFaultCode(new FaultCode("ServerException")));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
}
}
Стандартные исключения.Net правильно сериализованы на стороне сервера и десериализованы на стороне клиента. По умолчанию не наш. Зачем? Рекомендуется отправлять бизнес-исключение клиенту во время сеансов отладки:
- без необходимости помещать данные исключений в объект [DataMember]
- имея больше информации, чем простая строка (ExceptionFault<ExceptionDetail>
)
Но позаботьтесь о том, чтобы при отправке кода в производство не отправлялись исключения. Это может привести к утечкам в системе безопасности, раскрывающим подробности хакерам, если ваш сервис доступен в Интернете!
Чтобы отправить бизнес-исключение клиенту, наилучшими (и некоторыми обязательными) методами являются:
1 / Переключить serviceDebugBehavior
на
ServiceHost host = ...;
var debuggingBehavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
if (debuggingBehavior == null)
debuggingBehavior = new ServiceBehaviorAttribute();
#if DEBUG
debuggingBehavior.IncludeExceptionDetailInFaults = true;
#else
debuggingBehavior.IncludeExceptionDetailInFaults = false;
#endif
Это также довольно легко настроить в XML
2/ В интерфейсе службы объявите некоторый [FaultContract]:
[ServiceContract(Namespace="your namespace")]
public interface IBillingService
{
[OperationContract]
[FaultContract(typeof(BusinessException))]
void RaiseBusinessException();
}
3 / Бизнес-исключение должно быть помечено как Сериализуемый
[Serializable]
public class BusinessException : Exception
{ ... }
4/ Для того, чтобы бизнес-исключение было правильно десериализовано на стороне клиента как FaultException<BusinessException>
важно реализовать конструктор, заботящийся о десериализации. В противном случае вы получите общий FaultException
,
protected BusinessException(SerializationInfo info, StreamingContext context)
: base(info, context)
{}
5/ Если в вашем исключении есть несколько дополнительных членов, сериализуйте / десериализуйте их:
public DateTime CreationTime { get; set; }
protected BusinessException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
CreationTime = (DateTime)info.GetValue("CreationTime", typeof(DateTime));
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("CreationTime", CreationTime);
}