WCF: предоставить универсальное исключение FaultException в IErrorHandler
Немного контекста: у нас есть собственный XSD и мы генерируем код WSDL и C # с помощью WSCF.blue. Клиентская сторона используетChannelFactory<T>
и разделяет интерфейс, который включает все атрибуты, добавленные WSCF.blue, чтобы соответствовать тому, что находится в XSD.
Я пытаюсь реализоватьIErrorHandler.ProvideFault
где он предоставляет общийFaultException<T>
, но на стороне клиента я получаю не универсальныйFaultContract
, Это то, что мойProvideFault
Метод выглядит так:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (!(error is FaultException))
{
FaultException faultException = FaultExceptionFactory.CreateFaultException(error);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
}
}
В каждом методе обслуживания, если я делаю попытку / поймать сthrow FaultExceptionFactory.CreateFaultException(ex)
это работает, как и ожидалось, поэтому я думаю,[FaultContract]
, фабрика, крепления и т. д. все правильно. На всякий случай вот так работает фабрика:
BusinessRuleFaultExceptionType businessRuleFaultException = new BusinessRuleFaultExceptionType();
BusinessRuleFaultException.Code = exception.Code.ToString();
BusinessRuleFaultException.Reason = exception.Message;
return new FaultException<BusinessRuleFaultExceptionType>(
businessRuleFaultException,
exception.Message,
new FaultCode(exception.Code.ToString())
);
Я думаю, что проблема в том, как сообщение создается вIErrorHandler
возможно вCreateMessageFault()
, Я прочитал, что действие должно бытьfaultException.Action
вместоnull
, но по фактуfaultException.Action
являетсяnull
, Может быть, это вызывает проблему. Я могу установить действие на заводе, но каким должно быть это действие, и почему оно не появится при ручном броске?
Любые другие идеи, которые я мог бы упустить?
Редактировать: Я проверил WSDL и нашел конкретную операцию, которую я вызывал, и ее действие:
<wsdl:operation name="MyMethod">
<wsdl:fault wsaw:Action="http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault" name="BusinessRuleFaultExceptionTypeFault" message="tns:..."/>
Я старался кодировать действие:Message.CreateMessage(..., "http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault")
и установив его на.Action
на фабрике, но это все еще не сработало.
Изменить 2: Метод throw / catch генерирует следующий XML, который позволяет перехватить общее исключение на клиенте:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns="http://myNamespace/Services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Code xmlns="http://myNamespace/Entitites">-100</Code>
<Reason xmlns="http://myNamespace/Entitites">xxx</Reason>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
IHttpErrorHandler
генерирует следующее, что идет к неуниверсальномуFaultException
:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns="http://schemas.datacontract.org/2004/07/My.Dot.Net.Namespace" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<codeField>-100</codeField>
<reasonField>xxx</reasonField>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
Изменить 3: Если я добавлю[DataContract]
а также[DataMember]
кBusinessRuleFaultExceptionType
тогда я получаю почти правильный XML:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns="http://myNamespace/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Code>-100</Code>
<Reason>xxx</Reason>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
В нем отсутствует пространство имен для кода и причины. По крайней мере, я думаю, что это сужает это. Регулярная сериализация использует[XmlType]
а также[XmlElement]
в то время какIErrorHandler
использует[DataContract]
а также[DataMember]
, к несчастью[DataMember]
не позволяет вам установить пространство имен, поэтому я думаю, что теперь вопрос заключается в том, как использовать XMLSerializer вIErrorHandler
, Это описывает мою проблему, но исправление не будет работать по причине, указанной выше:http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html
Изменить 4: Я частично нашел проблему. Мы используемXmlSerializer
, но с тех порIErrorHandler
находится за пределами действия операции, возвращается к значению по умолчаниюDataContractSerializer
, Решение состоит в том, чтобы либо изменить свой сервис для использованияDataContractSerializer
везде или вручную выбратьXmlSerializer
при создании неисправностей. Эти две статьи предоставили то, что мне было нужно:
http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html http://zamd.net/2008/08/15/serializing-faults-using-xmlserializer/
Это получает меняочень близко. Он такой же, как и рабочий XML, за исключением того, что отсутствует пространство имен типа исключения:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Code xmlns="http://myNamespace/Entitites">-100</Code>
<Reason xmlns="http://myNamespace/Entitites">xxx</Reason>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
Я предполагаю, что это не добавляетxmlns="http://myNamespace/Services"
потому что у него нет контекста запроса. Это пространство имен определено в интерфейсе, но не в контракте данных. Я действительно буду вынужден оставаться в контексте запроса (надеюсь,IOperationInvoker
работает) вместо использованияIHttpHandler
?