Ich mag es, meine WCF-Service-Clients innerhalb eines using
Blocks zu instanziieren , da dies so ziemlich die Standardmethode für die Verwendung von Ressourcen ist, die Folgendes implementieren IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Wie in diesem MSDN-Artikel erwähnt , kann das Umschließen eines WCF-Clients in einen using
Block jedoch alle Fehler maskieren, die dazu führen, dass der Client in einem fehlerhaften Zustand bleibt (z. B. ein Timeout oder ein Kommunikationsproblem). Kurz gesagt, wenn Dispose () aufgerufen wird, wird die Close () -Methode des Clients ausgelöst, gibt jedoch einen Fehler aus, da sie sich in einem fehlerhaften Zustand befindet. Die ursprüngliche Ausnahme wird dann durch die zweite Ausnahme maskiert. Nicht gut.
Die im MSDN-Artikel vorgeschlagene Problemumgehung besteht darin, die Verwendung eines using
Blocks vollständig zu vermeiden und stattdessen Ihre Clients zu instanziieren und sie wie folgt zu verwenden:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
Im Vergleich zum using
Block finde ich das hässlich. Und jedes Mal, wenn Sie einen Client benötigen, müssen Sie viel Code schreiben.
Zum Glück habe ich einige andere Problemumgehungen gefunden, wie diese auf IServiceOriented. 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();
}
}
}
}
Was dann erlaubt:
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 der using
Block.
Die Problemumgehung, die ich derzeit verwenden möchte, habe ich zuerst auf blog.davidbarret.net gelesen . Grundsätzlich überschreiben Sie die Dispose()
Methode des Clients, wo immer Sie sie verwenden. Etwas wie:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
Dies scheint in der Lage zu sein, den using
Block wieder zuzulassen , ohne dass die Gefahr besteht, eine Fehlerzustandsausnahme zu maskieren.
Gibt es noch andere Fallstricke, auf die ich bei der Verwendung dieser Problemumgehungen achten muss? Hat sich jemand etwas Besseres ausgedacht?
Action<T>
anstelle von verwendenUseServiceDelegate<T>
. geringer.Service<T>
da er das Testen von Einheiten erschwert (wie die meisten statischen Dinge). Ich würde es vorziehen, nicht statisch zu sein, damit es in die Klasse eingefügt werden kann, die es verwendet.Antworten:
Obwohl ich gebloggt habe (siehe Lukes Antwort ), denke ich, dass dies besser ist als mein IDisposable-Wrapper. Typischer Code:
(per Kommentar bearbeiten)
Da
Use
Rückgaben ungültig sind, können Rückgabewerte am einfachsten über eine erfasste Variable verarbeitet werden:quelle
public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
undhttps://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/
undhttp://dzimchuk.net/post/wcf-error-helpers
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
Sie haben die Wahl zwischen der von IServiceOriented.com empfohlenen Lösung und der von David Barrets Blog empfohlenen Lösung bevorzuge ich die Einfachheit, die durch das Überschreiben der Dispose () -Methode des Clients geboten wird. Dadurch kann ich die using () -Anweisung weiterhin so verwenden, wie man es von einem Einwegobjekt erwarten würde. Wie @Brian hervorhob, enthält diese Lösung jedoch eine Race-Bedingung, da der Status möglicherweise nicht fehlerhaft ist, wenn er überprüft wird, sondern zum Zeitpunkt des Aufrufs von Close (). In diesem Fall tritt die CommunicationException weiterhin auf.
Um dies zu umgehen, habe ich eine Lösung eingesetzt, die das Beste aus beiden Welten mischt.
quelle
success
Flagge? Warum nichttry { Close(); } catch { Abort(); throw; }
?Close(); success = true;
? Ich würde nicht wollen, dass eine Ausnahme ausgelöst wird, wenn ich sie im finally-Block erfolgreich abbrechen könnte. Ich möchte nur, dass eine Ausnahme ausgelöst wird, wenn Abort () in diesem Fall fehlschlägt. Auf diese Weise würde der Versuch / Fang die mögliche Ausnahme für die Rennbedingungen verbergen und es Ihnen dennoch ermöglichen, die Verbindung im finally-Block abzubrechen ().Ich habe eine Funktion höherer Ordnung geschrieben , damit es richtig funktioniert. Wir haben dies in mehreren Projekten verwendet und es scheint großartig zu funktionieren. So hätten die Dinge von Anfang an gemacht werden sollen, ohne das Paradigma der "Verwendung" oder so weiter.
Sie können folgende Anrufe tätigen:
Dies ist so ziemlich genau wie in Ihrem Beispiel. In einigen Projekten schreiben wir stark typisierte Hilfsmethoden, sodass wir am Ende Dinge wie "Wcf.UseFooService (f => f ...)" schreiben.
Alles in allem finde ich es ziemlich elegant. Gibt es ein bestimmtes Problem, auf das Sie gestoßen sind?
Auf diese Weise können andere nützliche Funktionen angeschlossen werden. Beispielsweise authentifiziert sich die Site an einem Standort im Namen des angemeldeten Benutzers beim Dienst. (Die Site hat selbst keine Anmeldeinformationen.) Indem wir unseren eigenen "UseService" -Methodenhelfer schreiben, können wir die Channel-Factory nach unseren Wünschen konfigurieren usw. Wir sind auch nicht an die Verwendung der generierten Proxys gebunden - jede Schnittstelle reicht aus .
quelle
GetCachedFactory
Methode?Dies ist die von Microsoft empfohlene Methode zur Verarbeitung von WCF-Clientaufrufen:
Weitere Einzelheiten finden Sie unter: Erwartete Ausnahmen
Zusätzliche Informationen In WCF scheinen so viele Leute diese Frage zu stellen, dass Microsoft sogar ein spezielles Beispiel erstellt hat, um zu demonstrieren, wie mit Ausnahmen umgegangen wird:
c: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ client
Laden Sie das Beispiel herunter: C # oder VB
Bedenkt man, dass es so viele Fragen sind die Anweisung using beteiligt , (beheizt?) Interne Diskussionen und Themen zu diesem Thema, ich werde meine Zeit nicht verschwenden, einen Code Cowboy zu werden und einen sauberen Weg zu finden. Ich werde es einfach aufsaugen und WCF-Clients auf diese ausführliche (aber vertrauenswürdige) Weise für meine Serveranwendungen implementieren.
Optionale zusätzliche Fehler beim Fangen
Viele Ausnahmen ergeben sich aus
CommunicationException
und ich denke nicht, dass die meisten dieser Ausnahmen wiederholt werden sollten. Ich habe jede Ausnahme auf MSDN durchgesehen und eine kurze Liste von wiederholbaren Ausnahmen gefunden (zusätzlich zu denTimeOutException
oben genannten). Lassen Sie mich wissen, wenn ich eine Ausnahme verpasst habe, die wiederholt werden sollte.Zugegeben, dies ist ein bisschen banaler Code zum Schreiben. Ich bevorzuge derzeit diese Antwort und sehe keine "Hacks" in diesem Code, die später Probleme verursachen könnten.
quelle
"Hope this code wasn't important, because it might not happen."
wird noch ausgeführt ...Ich habe endlich einige solide Schritte in Richtung einer sauberen Lösung für dieses Problem gefunden.
Codeplex hat ein Projekt namens Exception Handling WCF Proxy Generator . Grundsätzlich wird ein neues benutzerdefiniertes Tool in Visual Studio 2008 installiert und anschließend mit diesem Tool der neue Service-Proxy generiert (Service-Referenz hinzufügen) . Es hat einige nette Funktionen, um mit fehlerhaften Kanälen, Zeitüberschreitungen und sicherer Entsorgung umzugehen. Hier gibt es ein ausgezeichnetes Video namens ExceptionHandlingProxyWrapper , das genau erklärt, wie das funktioniert.
Sie können die
Using
Anweisung sicher wieder verwenden. Wenn der Kanal bei einer Anforderung (TimeoutException oder CommunicationException) fehlerhaft ist, initialisiert der Wrapper den fehlerhaften Kanal neu und wiederholt die Abfrage. Wenn dies fehlschlägt, ruft es denAbort()
Befehl auf, entsorgt den Proxy und löst die Ausnahme erneut aus. Wenn der Dienst einenFaultException
Code auslöst, wird die Ausführung beendet und der Proxy wird abgebrochen, wobei die richtige Ausnahme wie erwartet ausgelöst wird.quelle
Basierend auf den Antworten von Marc Gravell, MichaelGG und Matt Davis haben unsere Entwickler Folgendes entwickelt:
Anwendungsbeispiel:
Es kommt der "using" -Syntax so nahe wie möglich, Sie müssen beim Aufrufen einer void-Methode keinen Dummy-Wert zurückgeben und können den Dienst mehrfach aufrufen (und mehrere Werte zurückgeben), ohne Tupel verwenden zu müssen.
ClientBase<T>
Falls gewünscht , können Sie dies auch mit Nachkommen anstelle von ChannelFactory verwenden.Die Erweiterungsmethode wird angezeigt, wenn ein Entwickler stattdessen einen Proxy / Kanal manuell entsorgen möchte.
quelle
DisposeSafely
Privat zu machen ist sicherlich eine Option und würde Verwirrung vermeiden. Es kann Anwendungsfälle geben, in denen jemand es direkt anrufen möchte, aber ich kann mir nicht ohne weiteres eine einfallen lassen.https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
@ Marc Gravell
Wäre es nicht in Ordnung, dies zu verwenden:
Oder das Gleiche
(Func<T, TResult>)
im Fall vonService<IOrderService>.Use
Dies würde die Rückgabe von Variablen erleichtern.
quelle
Was ist das?
Dies ist die CW-Version der akzeptierten Antwort, jedoch mit (was ich für vollständig halte) Ausnahmebehandlung.
Die akzeptierte Antwort verweist auf diese Website, die es nicht mehr gibt . Um Ihnen Ärger zu ersparen, füge ich hier die wichtigsten Teile hinzu. Darüber hinaus habe ich es geringfügig geändert , um die Behandlung von Ausnahmewiederholungen einzuschließen , um diese lästigen Netzwerk-Timeouts zu behandeln.
Einfache Verwendung des WCF-Clients
Sobald Sie Ihren clientseitigen Proxy generiert haben, ist dies alles, was Sie zur Implementierung benötigen.
ServiceDelegate.cs
Fügen Sie diese Datei Ihrer Lösung hinzu. An dieser Datei sind keine Änderungen erforderlich, es sei denn, Sie möchten die Anzahl der Wiederholungsversuche oder die Ausnahmen ändern, die Sie behandeln möchten.
PS: Ich habe diesen Beitrag zu einem Community-Wiki gemacht. Ich werde aus dieser Antwort keine "Punkte" sammeln, aber ich bevorzuge es, wenn Sie mit der Implementierung einverstanden sind oder sie bearbeiten, um sie besser zu machen.
quelle
success == false
der endgültigen if-Anweisung hinzuUnten finden Sie eine erweiterte Version der Quelle aus der Frage, die erweitert wurde, um mehrere Kanalfabriken zwischenzuspeichern und zu versuchen, den Endpunkt in der Konfigurationsdatei nach Vertragsnamen zu suchen.
Es verwendet .NET 4 (speziell: Kontravarianz, LINQ,
var
):quelle
UseServiceDelegate<T>
statt verwendenAction<T>
?Action<T>
funktioniert es genauso gut.Ein Wrapper wie dieser würde funktionieren:
Das sollte es Ihnen ermöglichen, Code wie folgt zu schreiben:
Der Wrapper könnte natürlich mehr Ausnahmen abfangen, wenn dies erforderlich ist, aber das Prinzip bleibt dasselbe.
quelle
Dispose
einen IChannel aufrufen, kann dies eine Ausnahme auslösen, wenn sich der Kanal in einem fehlerhaften Zustand befindet. Dies ist ein Problem, da Microsoft angibt, dass diesDispose
niemals ausgelöst werden soll. Der obige Code behandelt also den Fall, wennClose
eine Ausnahme ausgelöst wird. WennAbort
wirft, könnte etwas ernsthaft falsch sein. Ich habe letzten Dezember einen Blog-Beitrag darüber geschrieben: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapperIch habe den dynamischen Proxy von Castle verwendet, um das Dispose () -Problem zu lösen, und auch die automatische Aktualisierung des Kanals implementiert, wenn er sich in einem unbrauchbaren Zustand befindet. Um dies zu verwenden, müssen Sie eine neue Schnittstelle erstellen, die Ihren Servicevertrag und IDisposable erbt. Der dynamische Proxy implementiert diese Schnittstelle und umschließt einen WCF-Kanal:
Ich mag dies, da Sie WCF-Dienste einspeisen können, ohne dass sich Verbraucher um Details von WCF kümmern müssen. Und es gibt keine zusätzliche Kruft wie bei den anderen Lösungen.
Schauen Sie sich den Code an, es ist eigentlich ziemlich einfach: WCF Dynamic Proxy
quelle
Verwenden Sie eine Erweiterungsmethode:
quelle
Wenn Sie kein IoC benötigen oder einen automatisch generierten Client (Servicereferenz) verwenden, können Sie das Schließen einfach mit einem Wrapper verwalten und den GC die Clientbasis übernehmen lassen, wenn er sich in einem sicheren Zustand befindet, der keine Ausnahme auslöst. Der GC ruft Dispose in serviceclient auf und ruft auf
Close
. Da es bereits geschlossen ist, kann es keinen Schaden verursachen. Ich benutze dies ohne Probleme im Produktionscode.Wenn Sie dann auf den Server zugreifen, erstellen Sie den Client und verwenden ihn
using
im Autodisconect:quelle
Zusammenfassung
Mit den in dieser Antwort beschriebenen Techniken kann ein WCF-Dienst in einem using-Block mit der folgenden Syntax verwendet werden:
Sie können dies natürlich noch weiter anpassen, um ein präziseres Programmiermodell zu erhalten, das auf Ihre Situation
IMyService
zugeschnitten ist. Der Punkt ist jedoch, dass wir eine Implementierung der Repräsentation des Kanals erstellen können, die das Einwegmuster korrekt implementiert.Einzelheiten
Alle bisher gegebenen Antworten befassen sich mit dem Problem, den "Fehler" in der WCF-Kanal-Implementierung von zu umgehen
IDisposable
. Die Antwort, die das knappste Programmiermodell zu bieten scheint (so dass Sie die verwendenusing
Block auf nicht verwalteten Ressourcen zu verfügen) ist dies ein - wo der Proxy modifed zu implementieren istIDisposable
mit einer fehlerfreien Umsetzung. Das Problem bei diesem Ansatz ist die Wartbarkeit. Wir müssen diese Funktionalität für jeden von uns verwendeten Proxy erneut implementieren. Anhand einer Variation dieser Antwort werden wir sehen, wie wir Komposition anstelle von Vererbung verwenden können, um diese Technik generisch zu machen.Erster Versuch
Es scheint verschiedene Implementierungen für die
IDisposable
Implementierung zu geben, aber aus Gründen der Argumentation werden wir eine Anpassung der von der derzeit akzeptierten Antwort verwendeten verwenden .Mit den oben genannten Klassen können wir jetzt schreiben
Dies ermöglicht es uns, unseren Service über den
using
Block zu nutzen:Dies generisch machen
Bisher haben wir lediglich die Lösung von Tomas neu formuliert . Was verhindert, dass dieser Code generisch ist, ist die Tatsache, dass die
ProxyWrapper
Klasse für jeden gewünschten Servicevertrag neu implementiert werden muss. Wir werden uns nun eine Klasse ansehen, mit der wir diesen Typ mithilfe von IL dynamisch erstellen können:Mit unserer neuen Helferklasse können wir jetzt schreiben
Beachten Sie, dass Sie dieselbe Technik (mit geringfügigen Änderungen) auch für automatisch generierte Clients verwenden können, die für
ClientBase<>
(anstatt zu verwendenChannelFactory<>
) erben , oder wenn Sie eine andere Implementierung von verwenden möchten, umIDisposable
Ihren Kanal zu schließen.quelle
Ich mag diese Art, die Verbindung zu schließen:
quelle
Ich habe eine einfache Basisklasse geschrieben , die dies behandelt. Es ist als NuGet-Paket erhältlich und recht einfach zu bedienen.
quelle
So können Sie return-Anweisungen gut schreiben:
quelle
Ich möchte die Implementierung von Service aus Marc Gravells Antwort für den Fall hinzufügen, dass ServiceClient anstelle von ChannelFactory verwendet wird.
quelle
Für Interessierte ist hier eine VB.NET-Übersetzung der akzeptierten Antwort (unten). Ich habe es der Kürze halber etwas verfeinert und einige der Tipps anderer in diesem Thread kombiniert.
Ich gebe zu, dass es für die Ursprungs-Tags (C #) kein Thema ist, aber da ich keine VB.NET-Version dieser guten Lösung finden konnte, gehe ich davon aus, dass auch andere suchen werden. Die Lambda-Übersetzung kann etwas knifflig sein, deshalb möchte ich jemandem die Mühe ersparen.
Beachten Sie, dass diese spezielle Implementierung die Möglichkeit bietet, die zur
ServiceEndpoint
Laufzeit zu konfigurieren .Code:
Verwendungszweck:
quelle
Unsere Systemarchitektur verwendet häufig das Unity IoC- Framework, um Instanzen von ClientBase zu erstellen, sodass es keine sichere Möglichkeit gibt, zu erzwingen, dass die anderen Entwickler sogar
using{}
Blöcke verwenden. Um es so narrensicher wie möglich zu machen, habe ich diese benutzerdefinierte Klasse erstellt, die ClientBase erweitert und das Schließen des Kanals beim Entsorgen oder beim Finalisieren behandelt, falls jemand die von Unity erstellte Instanz nicht explizit entsorgt.Es gibt auch Dinge, die im Konstruktor erledigt werden mussten, um den Kanal für benutzerdefinierte Anmeldeinformationen und Dinge einzurichten, also ist das auch hier ...
Dann kann ein Kunde einfach:
Und der Anrufer kann Folgendes tun:
quelle
Ich habe nur wenige Antworten auf diesen Beitrag gegeben und ihn an meine Bedürfnisse angepasst.
Ich wollte die Möglichkeit haben, etwas mit dem WCF-Client zu tun, bevor ich ihn verwende, also die
DoSomethingWithClient()
Methode.Hier ist die Helferklasse:
Und ich kann es verwenden als:
quelle
Ich habe meinen eigenen Wrapper für einen Kanal, der Dispose wie folgt implementiert:
Dies scheint gut zu funktionieren und ermöglicht die Verwendung eines using-Blocks.
quelle
Der folgende Helfer ermöglicht das Aufrufen
void
und Nicht-Leeren von Methoden. Verwendungszweck:Die Klasse selbst ist:
quelle
Überschreiben Sie Dispose () des Clients, ohne eine auf ClientBase basierende Proxy-Klasse generieren zu müssen, auch ohne die Erstellung und das Caching von Kanälen verwalten zu müssen ! (Beachten Sie, dass WcfClient keine ABSTRACT-Klasse ist und auf ClientBase basiert.)
quelle
Meine Methode bestand darin, eine geerbte Klasse zu erstellen, die IDisposable explizit implementiert. Dies ist nützlich für Leute, die die GUI verwenden, um die Dienstreferenz hinzuzufügen (Dienstreferenz hinzufügen). Ich lasse diese Klasse einfach im Projekt fallen und erstelle die Dienstreferenz und verwende sie anstelle des Standardclients:
Hinweis: Dies ist nur eine einfache Implementierung von dispose. Sie können eine komplexere Dispose-Logik implementieren, wenn Sie möchten.
Sie können dann alle Ihre mit dem regulären Service-Client getätigten Anrufe durch die sicheren Clients wie folgt ersetzen:
Ich mag diese Lösung, da ich keinen Zugriff auf die Schnittstellendefinitionen haben muss und die
using
Anweisung wie erwartet verwenden kann, während mein Code mehr oder weniger gleich aussieht.Sie müssen weiterhin die Ausnahmen behandeln, die ausgelöst werden können, wie in anderen Kommentaren in diesem Thread ausgeführt.
quelle
Sie können auch eine verwenden
DynamicProxy
, um dieDispose()
Methode zu erweitern . Auf diese Weise können Sie Folgendes tun:quelle