ReceivedFault is not marked as serializable

Minulý týden jsem potřeboval použít v AX2009 jednu webovou službu poskytovanou třetí stranou. Vytvořil jsem referenci v AOT, takže AX vygenerovala potřebný kód a konfiguraci a já jsem byl okamžitě schopný získat data z webové služby. Skvělé.

Ale nepsal bych o tom, pokud by to bylo všechno. Ta daná webová služba se bude používat interaktivně – zadáte nějaký vstup, zavolá se webová služba, zobrazí se data a tak dále. Někdy také zadáte špatný vstup a služba vrátí speciální zprávu s kódem chyby a textem popisujícím vzniklý problém.

Očekával jsem, že dostanu FaultException, kterou bych odchytil a nějak zpracoval. Namísto toho jsem dostal tuto chybu:

Type ‘System.ServiceModel.Channels.ReceivedFault’ in assembly ‘System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ is not marked as serializable.

Zkusil jsem hledat na webu a očividně nejsem první, kdo se s tímto problém setkal, ale nenašel jsem žádné užitečné vysvětlení nebo řešení.

Tak jsem se podíval do použité implementace a zjistil jsem, že proxy vygenerovaná Axaptou dědí od třídy Microsoft.Dynamics.IntegrationFramework.WebService.WebReferenceBase (v Microsoft.Dynamics.IntegrationFramework.dll). Tato třída vytvoří pro každou webovou službu novou aplikační doménu, což umožňuje – když nic jiného – nahrát soubor app.config, ale hranice aplikační domény očividně způsobuje problém s instancí ReceivedFault.

Nenapadá mě žádný způsob, jak použít referenci generovanou Axaptou a zároveň se vyhnout chybě v serializaci. Reference na služby mají v AX pěkně integrovanou konfiguraci, ale v mém případě je náležité zpracování výjimek důležitější. Takže jsem vytvořil samostatnou knihovnu tříd, která referencuje danou webovou službu, nastavuje binding a adresu endpointu a volá webovou službu.

Pominu-li konfiguraci a validaci vstupu, vypadá celá implementace takto jednoduše:

public static Object GetData(string input)
{
    using (var client = new MyService_SoapClient(
        new BasicHttpBinding(),
        new EndpointAddress("http://somewhere/service.ws")))
    {
        return client.GetData(input);
    }
}

ReceivedFault je korektně transformována na FaultException, která je pak poslána do AX, kde je možné ji bez problému odchytit.

Sice jsem nezkoušel použít stejnou webovou službu v AX2012, ale nepředpokládám, že by tam byl stejný problém. AX2012 totiž řeší webové služby podstatně jinak než AX2009.