Sellest, et WCF proxy saab using-blokiga halvasti läbi..

On saanud harjumuseks IDisposable tüüpe kasutades kohmaka try-finally bloki asemel ilusat using-süntaksit kasutada. Paraku selgus, et Windows Communication Foundation ei ole selle suhtes väga sõbralikult meelestatud juhul kui teenuse tarbimise käigus tekib mõni viga WCF kanaliga. Näiteks:

using (var usingClient = new FtpProxyServiceClient())
{
    usingClient.RecieveFile(target); //this call throws an exception
}

tagastab võrdlemisi kasutu veateate:

System.ServiceModel.CommunicationObjectFaultedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

Server stack trace:
   at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.Close()
   at System.ServiceModel.ClientBase`1.System.IDisposable.Dispose()
   at SomeNameSpace.UIConsole.Program.Test() in W:\SomePath\UIConsole\Program.cs:line 32

Pöörakem tähelepanu, et vea põhjuse või tekkekoha kohta ei ole jälgegi, vaid viga näib tulevat hoopis Dispose() väljakutsumisel. Selgub, et WCF proxy Close() ebaõnnestub kui eelneva vea tõttu on ühendus juba maha võetud. Tulemuseks on UUS viga ja vana unustatakse hoopis.

Lahenduseks on tagasipöördumine try-finally lahenduse poole ja Abort() kasutamine:

var tryClient = new FtpProxyServiceClient();
Boolean succeeded = false;
try
{
    tryClient.RecieveFile(target); //this call throws an exception
    tryClient.Close();
    succeeded = true;
}
finally
{
    if (!succeeded) { tryClient.Abort(); }
}

Ehk vea järel kutsume Close() asemel välja Abort() meetodi. Selle peale saame lõpuks ometi teada ka algse vea põhjuse, asjaosalise stackframe'i jne:

System.ServiceModel.CommunicationException: The maximum message size quota for incoming messages (20) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element. ---> System.ServiceModel.QuotaExceededException: The maximum message size quota for incoming messages (20) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
   --- End of inner exception stack trace ---

[ - skipped some nonrelevant stacktrace entries - ]

   at SomeNameSpace.Ftp.IFtpProxyService.RecieveFile(Uri targetFile)
   at SomeNameSpace.Ftp.FtpProxyServiceClient.RecieveFile(Uri targetFile) in w:\SomePath\ServiceClients.Ftp\Reference.cs:line 102
   at SomeNameSpace.UIConsole.Program.Test()
                 

Kui jõudlus ei ole probleemiks siis funktsionaalselt enam-vähem samaväärne on õnnestumise jälgimine ära jätta ja Abort() alati välja kutsuda. Viga ta ei viska, aga lisandub väike performance overhead ja see oleks ka lihtsalt vale ;)

Abiks oli Damian McGivern'i postitus, kus muuhulgas vihjatakse, kuidas seda try-finally-succeeded blokki mugavamaks teha saaks.

Huvitav, kas c# 4.0 jaoks on WCF proxy Close() pisut koostööaltimaks tehtud ..

Kommentaare ei ole: