WCF виноват контракты в реальном мире - PullRequest
14 голосов
/ 06 января 2012

Предположим, у нас есть метод сервиса, который выполняет некоторые проверки безопасности, извлекает данные из БД и стороннего веб-сервиса, создает MyDataDTO, записывает контрольную запись обратно в БД. И мы хотим хорошо структурированные, детальные коды ошибок, не так ли? Мы хорошие ребята и следуем стандартным правилам обработки ошибок WCF:

[FaultContract(typeof(AccessDenied))]
[FaultContract(typeof(KeyNotFound))]
[FaultContract(typeof(WsFault))]
[FaultContract(typeof(DbFault))]
MyDataDTO GetData(string key);

Теперь мы добавляем новый метод, который обновляет данные. Метод вызывает GetData() внутренне (или большую его часть), выполняет проверку, добавляет обновления данных. Таким образом, он должен иметь все ошибки GetData(), дублированные, и добавлять свои собственные ошибки:

[FaultContract(typeof(InvalidState))]
[FaultContract(typeof(DataNotValid))]
[FaultContract(typeof(AccessDenied))]
[FaultContract(typeof(KeyNotFound))]
[FaultContract(typeof(WsFault))]
[FaultContract(typeof(DbFault))]
void UpdateData(MyDataDTO data);

Пока все хорошо. Это позволяет нам даже создавать XML-документы, которые мы можем предоставить потребителям нашего сервиса, чтобы они знали, какие коды ошибок они могут ожидать.

Теперь представьте, что у нас есть 10 сервисов с 10 методами, как указано выше (или даже более сложными) каждый. И определение всех этих договоров о сбоях становится кошмаром, поскольку это довольно подверженный ошибкам процесс:

  1. Нет способа определить общие неисправности (например, DbFault) для всего сервиса
  2. Вы не можете гарантировать, что ошибка, определенная в операционном контракте, действительно будет возвращена (проблемы копирования и вставки)
  3. Вы не можете гарантировать, что не пропустили какую-то ошибку, чтобы добавить к договору операции

Не будем здесь учитывать версии интерфейса:)

Итак, вы получили представление о том, поддерживаете ли вы сервисы WCF на производстве. Должны ли мы вообще отказаться от контрактов на ошибки и использовать старый добрый C-стиль (например, иметь базовый класс DTOBase со свойством ErrorCode)? Уменьшить ошибку детализации? Как убедиться, что документация верна / актуальна? Я заинтересован в некоторых лучших практиках.

Ответы [ 2 ]

9 голосов
/ 13 мая 2013

Одна из проблем вашего первоначального подхода заключается в том, что вы пытаетесь воспроизвести множество системных ошибок / исключений, которые могут возникнуть. Поскольку сложность системы увеличивается с каждой новой функцией, число возможных проблем, которые вы должны учитывать, возрастает экспоненциально (или больше!)

Я бы предложил следующий подход: поскольку вы создаете «систему» ​​служб и вызовов доступа, определяйте только FaultContracts, относящиеся к этой системе. Клиентов должны интересовать только следующие вопросы:

  1. Это проблема с данными, которые я хотел, или способом, которым я спросил?
  2. Если нет, это проблема со мной, или это проблема, связанная с ИТ (сбой системы, ошибка сети, проблема с базой данных и т. Д.)?

С небольшими переделками вы можете сократить количество контрактов на отказ, которые вы должны предоставить. Например (и это не в моей голове):

//Base class to define general problems
[DataContract]
public class SysFault
{
  //MyDataDTO-specific general error.
  [DataMember]
  public string SysMsg {get;set;}

  //boolean for "this is a problem with me or the hosting system?"
  [DataMember]
  public bool IsSystemic {get;set;}
}

//Subclass to expose synchronization issues--if that's one you want to define
[DataContract]
public class SyncFault : SysFault
{
  [DataMember]
  public string SyncMsg { get;set; }
}

//Subclass to expose validation issues
[DataContract]
public class ValFault : SysFault
{
  [DataMember]
  public string ValMsg { get;set; }
}

Теперь вы можете использовать типы ошибок, чтобы отделить то, что происходит с вашими услугами. Например:

[ServiceContract]
public interface IRecordSys
{
    [OperationContract]
    [FaultContract(typeof(SysFault))]  //Raised for underlying problem
    [FaultContract(typeof(ValFault))]  //Raised if there is an issue with the key value
    MyDataDTO getData(string key);

    [OperationContract]
    [FaultContract(typeof(SysFault))]  //Raised for underlying problem elsewhere
    //Raised for some issue, such as unable to get two subsystems to update properly
    //with the given data
    [FaultContract(typeof(SyncFault))]
    void update(MyDataDTO data); 
}

Ваша конкретная реализация будет отличаться, но идея в том, чтобы передавать сообщения, касающиеся вашей системы, а не каждую небольшую системную проблему, которая может возникнуть.

1 голос
/ 06 января 2012

Ну, вы можете реализовать это:

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)

В этом случае у вас будет одно место, где вы будете переключаться по типу исключения / сообщению и сообщать свои ошибки.

http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler.providefault.aspx

или еще лучше с образцами:

http://blogs.msdn.com/b/pedram/archive/2008/01/25/wcf-error-handling-and-some-best-practices.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...