Ich instanziere meine WCF-Dienst-Clients gerne innerhalb einer using
Block, da dies so ziemlich die Standardmethode zur Verwendung von Ressourcen ist, die die IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Aber, wie bereits in dieser MSDN-Artikel einen WCF-Client in eine using
Block könnte alle Fehler maskieren, die dazu führen, dass der Client in einem fehlerhaften Zustand verbleibt (wie eine Zeitüberschreitung oder ein Kommunikationsproblem). Langer Rede kurzer Sinn, wenn Dispose()
aufgerufen wird, wird der Client Close()
Methode wird ausgelöst, löst aber einen Fehler aus, weil sie sich in einem fehlerhaften Zustand befindet. Die ursprüngliche Ausnahme wird dann von der zweiten Ausnahme überdeckt. Das ist nicht gut.
Die im MSDN-Artikel vorgeschlagene Abhilfe besteht darin, die Verwendung einer using
Block zu verwenden, und stattdessen Ihre Clients zu instanziieren und sie etwa so zu verwenden:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
Verglichen mit dem using
Block, ich finde das hässlich. Und eine Menge Code zu schreiben, jedes Mal, wenn Sie einen Client benötigen.
Glücklicherweise habe ich einige andere Umgehungsmöglichkeiten gefunden, wie z. B. diese auf dem (jetzt nicht mehr existierenden) IServiceOriented Blog. Sie beginnen mit:
public delegate void UseServiceDelegate<T>(T proxy);
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
Das erlaubt dann:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
Das ist nicht schlecht, aber ich denke nicht, dass es so ausdrucksstark und leicht verständlich ist wie die using
Block.
Die Abhilfe, die ich derzeit versuche zu verwenden, habe ich zuerst auf blog.davidbarret.net . Im Grunde überschreiben Sie die Client Dispose()
Methode, wo immer Sie sie verwenden. Etwa so:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
Dies scheint es zu ermöglichen, die using
wieder blockieren, ohne dass die Gefahr besteht, eine fehlerhafte Zustandsausnahme zu maskieren.
Gibt es noch andere Probleme, auf die ich bei der Verwendung dieser Workarounds achten muss? Ist jemandem etwas Besseres eingefallen?