Обработка исключений с помощью сценария 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);
}
Другие вопросы по тегам