Lubię tworzyć wystąpienia moich klientów usług WCF w using
bloku, ponieważ jest to prawie standardowy sposób korzystania z zasobów, które implementują IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Ale, jak zauważono w tym artykule MSDN , zawijanie klienta WCF w plikuusing
bloku może maskować wszelkie błędy, które powodują pozostawienie stanie błędu (np. Przekroczenie limitu czasu lub problem z komunikacją). Krótko mówiąc, gdy wywoływana jest funkcja Dispose (), metoda Close () klienta uruchamia się, ale generuje błąd, ponieważ jest w stanie błędu. Oryginalny wyjątek jest następnie maskowany przez drugi wyjątek. Niedobrze.
Sugerowanym obejściem w artykule MSDN jest całkowite unikanie korzystania z using
bloku, a zamiast tego tworzenie instancji klientów i używanie ich w następujący sposób:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
W porównaniu do using
bloku myślę, że to brzydkie. I dużo kodu do napisania za każdym razem, gdy potrzebujesz klienta.
Na szczęście znalazłem kilka innych obejść, takich jak to na IServiceOriented. Zaczynasz z:
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();
}
}
}
}
Co następnie pozwala:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
To nie jest złe, ale nie sądzę, aby było tak wyraziste i łatwo zrozumiałe jak using
blok.
Obejście, którego obecnie próbuję użyć, najpierw przeczytałem na blog.davidbarret.net . Zasadniczo zastępujesz Dispose()
metodę klienta, gdziekolwiek jej używasz. Coś jak:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
Wydaje się, że jest to w stanie zezwolić na using
blok ponownie bez niebezpieczeństwa maskowania wyjątku stanu z błędem.
Czy są jakieś inne problemy, na które muszę uważać, korzystając z tych obejść? Czy ktoś wymyślił coś lepszego?
Action<T>
zamiastUseServiceDelegate<T>
. mniejszy.Service<T>
ponieważ komplikuje testowanie jednostkowe (jak robi to większość statycznych rzeczy). Wolałbym, aby był niestatyczny, aby mógł zostać wstrzyknięty do klasy, która go używa.Odpowiedzi:
Rzeczywiście, chociaż na blogu (patrz odpowiedź Luke'a ), myślę, że ten jest lepszy niż mój IDisposable owijki. Typowy kod:
(edytuj według komentarzy)
Ponieważ
Use
zwroty są nieważne, najłatwiejszym sposobem obsługi zwracanych wartości jest przechwycenie zmiennej:źródło
public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
ihttps://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/
ihttp://dzimchuk.net/post/wcf-error-helpers
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
Biorąc pod uwagę wybór między rozwiązaniem zalecanym przez IServiceOriented.com a rozwiązaniem zalecanym przez blog Davida Barreta , wolę prostotę oferowaną przez zastąpienie metody Dispose () klienta. To pozwala mi nadal używać instrukcji using (), jak można by się spodziewać po obiekcie jednorazowym. Jednak, jak wskazał @Brian, to rozwiązanie zawiera warunek wyścigu, w którym stan może nie być obwiniony, gdy jest sprawdzany, ale może być do czasu wywołania funkcji Close (), w którym to przypadku nadal występuje wyjątek CommunicationException.
Aby obejść ten problem, zastosowałem rozwiązanie, które łączy najlepsze cechy obu światów.
źródło
success
flagi? Dlaczego nietry { Close(); } catch { Abort(); throw; }
?Close(); success = true;
? Nie chciałbym, aby wyjątek został zgłoszony, gdybym mógł z powodzeniem przerwać go w ostatnim bloku. Chciałbym, aby wyjątek został zgłoszony tylko wtedy, gdy przerwanie () nie powiodło się w tym przypadku. W ten sposób try / catch ukryje potencjalny wyjątek warunku wyścigu i nadal pozwoli ci przerwać () połączenie w ostatnim bloku.Napisałem funkcję wyższego rzędu, aby działała poprawnie. Wykorzystaliśmy to w kilku projektach i wydaje się, że działa świetnie. Tak właśnie powinno było być od samego początku, bez paradygmatu „używania” lub tak dalej.
Możesz wykonywać takie połączenia:
To jest prawie tak jak w twoim przykładzie. W niektórych projektach piszemy silnie typowane metody pomocnicze, więc w końcu piszemy takie rzeczy jak „Wcf UseFooService (f => f ...)”.
Uważam to za dość eleganckie, biorąc pod uwagę wszystko. Czy napotkałeś szczególny problem?
Umożliwia to podłączenie innych fajnych funkcji. Na przykład w jednej witrynie witryna uwierzytelnia się w usłudze w imieniu zalogowanego użytkownika. (Strona sama w sobie nie ma poświadczeń.) Pisząc własnego pomocnika metody „UseService”, możemy skonfigurować fabrykę kanałów tak, jak chcemy, itp. Nie jesteśmy również zobowiązani do korzystania z generowanych serwerów proxy - dowolny interfejs zrobi .
źródło
GetCachedFactory
metoda?Jest to zalecany przez Microsoft sposób obsługi połączeń klienckich WCF:
Aby uzyskać więcej informacji, zobacz: Oczekiwane wyjątki
Dodatkowe informacje Wydaje się, że tak wiele osób zadaje to pytanie w WCF, że Microsoft stworzył nawet dedykowaną próbkę, aby zademonstrować, jak radzić sobie z wyjątkami:
c: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ client
Pobierz próbkę: C # lub VB
Biorąc pod uwagę, że istnieje tak wiele kwestii z udziałem using , (podgrzewany?) Wewnętrzne dyskusje i wątki na ten temat, nie będę tracić czasu próbuje stać kowboja kodu i znaleźć sposób czystszy. Po prostu wyssę to i zaimplementuję klientów WCF w ten pełny (ale zaufany) sposób dla moich aplikacji serwerowych.
Opcjonalne dodatkowe niepowodzenia wychwytywania
Wywodzi się wiele wyjątków
CommunicationException
i nie sądzę, że większość z tych wyjątków powinna zostać ponowiona. Przejrzałem każdy wyjątek na MSDN i znalazłem krótką listę wyjątków, które można ponownie spróbować (opróczTimeOutException
powyższych). Daj mi znać, jeśli przegapiłem wyjątek, który należy ponowić.Trzeba przyznać, że jest to trochę przyziemny kod do napisania. Obecnie wolę tę odpowiedź i nie widzę żadnych „hacków” w tym kodzie, które mogłyby powodować problemy w przyszłości.
źródło
"Hope this code wasn't important, because it might not happen."
jest nadal wykonywana ...W końcu znalazłem solidne kroki w kierunku czystego rozwiązania tego problemu.
Codeplex ma projekt o nazwie Exception Handling WCF Proxy Generator . Zasadniczo instaluje nowe niestandardowe narzędzie w programie Visual Studio 2008, a następnie używa tego narzędzia do generowania nowego serwera proxy usługi (Dodaj odniesienie do usługi) . Ma fajną funkcjonalność do radzenia sobie z uszkodzonymi kanałami, przekroczeniami czasu i bezpieczną utylizacją. Jest tutaj doskonały film wideo o nazwie ExceptionHandlingProxyWrapper, wyjaśniający dokładnie, jak to działa.
Możesz bezpiecznie użyć
Using
instrukcji ponownie, a jeśli kanał zostanie uszkodzony w przypadku dowolnego żądania (TimeoutException lub CommunicationException), Wrapper ponownie zainicjuje uszkodzony kanał i ponowi kwerendę. Jeśli to się nie powiedzie, wywołaAbort()
polecenie i usunie proxy i ponownie zwróci wyjątek. Jeśli usługa wyrzuci aFaultException
kod, przestanie działać, a serwer proxy zostanie bezpiecznie przerwany, zgłaszając poprawny wyjątek zgodnie z oczekiwaniami.źródło
Na podstawie odpowiedzi Marc Gravell, MichaelGG i Matt Davis nasi programiści wymyślili:
Przykład zastosowania:
Jest tak zbliżony do składni „using”, jak to możliwe, nie musisz zwracać wartości fikcyjnej podczas wywoływania metody void i możesz wykonywać wiele wywołań usługi (i zwracać wiele wartości) bez konieczności używania krotek.
Możesz także użyć tego z
ClientBase<T>
potomkami zamiast ChannelFactory, jeśli chcesz.Metoda rozszerzenia jest ujawniana, jeśli programista chce zamiast tego ręcznie pozbyć się proxy / kanału.
źródło
DisposeSafely
jest z pewnością opcją i pozwoli uniknąć zamieszania. Mogą istnieć przypadki użycia, w których ktoś chciałby zadzwonić bezpośrednio, ale nie mogę wymyślić jednego od ręki.https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
@Marc Gravell
Czy nie byłoby dobrze użyć tego:
Lub to samo
(Func<T, TResult>)
w przypadkuService<IOrderService>.Use
Ułatwi to zwracanie zmiennych.
źródło
Co to jest?
To jest wersja CW zaakceptowanej odpowiedzi, ale z (co uważam za kompletne) dołączoną obsługą wyjątków.
Przyjęta odpowiedź odnosi się do tej witryny, której już nie ma . Aby zaoszczędzić ci kłopotów, zamieszczam tutaj najbardziej odpowiednie części. Ponadto zmodyfikowałem go nieco, aby uwzględnić obsługę ponownych prób wyjątków w celu obsługi tych nieznośnych limitów czasu w sieci.
Proste użycie klienta WCF
Po wygenerowaniu serwera proxy po stronie klienta wystarczy go zaimplementować.
ServiceDelegate.cs
Dodaj ten plik do swojego rozwiązania. Ten plik nie wymaga żadnych zmian, chyba że chcesz zmienić liczbę ponownych prób lub jakie wyjątki chcesz obsłużyć.
PS: Stworzyłem ten post jako wiki społeczności. Nie będę zbierać „punktów” z tej odpowiedzi, ale wolę głosować za nią, jeśli zgadzasz się z implementacją, lub edytować ją, aby była lepsza.
źródło
success == false
do ostatecznej instrukcji ifPoniżej znajduje się ulepszona wersja źródła z pytania i rozszerzona o buforowanie fabryk wielu kanałów i próba wyszukania punktu końcowego w pliku konfiguracyjnym według nazwy kontraktu.
Wykorzystuje .NET 4 (w szczególności: contravariance, LINQ,
var
):źródło
UseServiceDelegate<T>
zamiastAction<T>
?Action<T>
działa równie dobrze.Takie opakowanie działałoby:
To powinno umożliwić Ci napisanie kodu takiego jak:
Opakowanie może oczywiście wychwycić więcej wyjątków, jeśli jest to wymagane, ale zasada pozostaje taka sama.
źródło
Dispose
kanał IChannel, może on zgłosić wyjątek, jeśli kanał jest w stanie błędu, jest to problem, ponieważ Microsoft określa, żeDispose
nigdy nie powinien generować. Tak więc powyższy kod zajmuje się sprawą, gdyClose
zgłasza wyjątek. JeśliAbort
rzuca, może to być coś poważnie złego. W grudniu napisałem o tym post na blogu: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapperUżyłem dynamicznego proxy Castle do rozwiązania problemu Dispose (), a także wdrożyłem automatyczne odświeżanie kanału, gdy jest on w stanie bezużytecznym. Aby z tego skorzystać, musisz utworzyć nowy interfejs, który odziedziczy umowę o świadczenie usług i IDisposable. Dynamiczny serwer proxy implementuje ten interfejs i otacza kanał WCF:
Podoba mi się to, ponieważ możesz wstrzykiwać usługi WCF bez potrzeby martwienia się o szczegóły WCF przez konsumentów. I nie ma dodanego cruftu, jak inne rozwiązania.
Spójrz na kod, w rzeczywistości jest dość prosty: WCF Dynamic Proxy
źródło
Użyj metody rozszerzenia:
źródło
Jeśli nie potrzebujesz IoC lub korzystasz z automatycznie generowanego klienta (Service Reference), możesz po prostu użyć otoki do zarządzania zamykaniem i pozwolić GC przejąć bazę klientów, gdy jest ona w stanie bezpiecznym, który nie spowoduje wyjątku. GC wywoła Dispose in service service, a to zadzwoni
Close
. Ponieważ jest już zamknięty, nie może powodować żadnych szkód. Używam tego bez problemów w kodzie produkcyjnym.Następnie, kiedy uzyskujesz dostęp do serwera, tworzysz klienta i używasz
using
w autodisconect:źródło
Podsumowanie
Korzystając z technik opisanych w tej odpowiedzi, można korzystać z usługi WCF w bloku używającym o następującej składni:
Możesz oczywiście dostosować to jeszcze bardziej, aby uzyskać bardziej zwięzły model programowania specyficzny dla twojej sytuacji - ale chodzi o to, że możemy stworzyć implementację
IMyService
reprentowania kanału, która poprawnie implementuje wzorzec jednorazowy.Detale
Wszystkie dotychczasowe odpowiedzi dotyczą problemu obejścia „błędu” w implementacji kanału WCF
IDisposable
. Odpowiedź, która wydaje się oferować najbardziej zwięzły model programistyczny (pozwalający na użycieusing
bloku do zarządzania niezarządzanymi zasobami), brzmi następująco - gdzie proxy jest modyfikowane do implementacjiIDisposable
z implementacją bezbłędną . Problemem w tym podejściu jest łatwość konserwacji - musimy ponownie wdrożyć tę funkcjonalność dla każdego serwera proxy, którego używamy. W odmianie tej odpowiedzi zobaczymy, w jaki sposób możemy używać kompozycji zamiast dziedziczenia, aby uczynić tę technikę ogólną.Pierwsze podejscie
Wydaje się, że istnieją różne implementacje do
IDisposable
implementacji, ale dla argumentu użyjemy adaptacji tej, która jest stosowana w obecnie akceptowanej odpowiedzi .Uzbrojeni w powyższe klasy możemy teraz pisać
To pozwala nam korzystać z naszych usług za pomocą
using
bloku:Czyniąc to ogólnym
Wszystko, co do tej pory zrobiliśmy, to przeformułowanie rozwiązania Tomasa . Tym, co uniemożliwia generowanie tego kodu, jest fakt, że
ProxyWrapper
klasa musi zostać ponownie zaimplementowana dla każdej umowy serwisowej, jakiej chcemy. Przyjrzymy się teraz klasie, która pozwala nam dynamicznie tworzyć ten typ za pomocą IL:Dzięki naszej nowej klasie pomocników możemy teraz pisać
Pamiętaj, że możesz również użyć tej samej techniki (z niewielkimi modyfikacjami) w przypadku automatycznie generowanych klientów dziedziczących po
ClientBase<>
(zamiast używaniaChannelFactory<>
) lub jeśli chcesz użyć innej implementacjiIDisposable
do zamknięcia kanału.źródło
Podoba mi się ten sposób zamykania połączenia:
źródło
Napisałem prostą klasę podstawową, która to obsługuje. Jest dostępny jako pakiet NuGet i jest dość łatwy w użyciu.
źródło
Pozwala to ładnie pisać zwroty:
źródło
Chciałbym dodać implementację usługi z odpowiedzi Marca Gravella na wypadek użycia ServiceClient zamiast ChannelFactory.
źródło
Dla zainteresowanych, oto tłumaczenie zaakceptowanej odpowiedzi VB.NET (poniżej). Udoskonaliłem to trochę dla zwięzłości, łącząc niektóre wskazówki innych w tym wątku.
Przyznaję, że jest to nie na temat oryginalnych tagów (C #), ale ponieważ nie mogłem znaleźć wersji tego doskonałego rozwiązania VB.NET, zakładam, że inni też będą szukać. Tłumaczenie Lambda może być nieco trudne, więc chciałbym komuś zaoszczędzić kłopotów.
Należy pamiętać, że ta konkretna implementacja zapewnia możliwość skonfigurowania
ServiceEndpoint
środowiska wykonawczego.Kod:
Stosowanie:
źródło
Nasza architektura systemu często używa frameworku Unity IoC do tworzenia instancji ClientBase, więc nie ma pewności, jak wymusić, aby inni programiści korzystali nawet z
using{}
bloków. Aby uczynić go tak głupim, jak to możliwe, stworzyłem tę niestandardową klasę, która rozszerza ClientBase i obsługuje zamykanie kanału podczas usuwania lub finalizacji, na wypadek, gdyby ktoś nie pozbył się wyraźnie instancji utworzonej przez Unity.W konstruktorze trzeba też wykonać pewne czynności, aby skonfigurować kanał dla niestandardowych danych uwierzytelniających i innych rzeczy, więc tutaj też ...
Następnie klient może po prostu:
Dzwoniący może wykonać dowolną z tych czynności:
źródło
Odniosłem się do kilku odpowiedzi w tym poście i dostosowałem go do moich potrzeb.
Chciałem mieć możliwość zrobienia czegoś z klientem WCF przed użyciem go, więc
DoSomethingWithClient()
metoda.Oto klasa pomocnicza:
I mogę go użyć jako:
źródło
Mam własne opakowanie dla kanału, który implementuje Dispose w następujący sposób:
Wydaje się, że działa to dobrze i pozwala na użycie bloku używającego.
źródło
Następujący pomocnik pozwala na wywoływanie
void
i nieważne metody. Stosowanie:Sama klasa to:
źródło
Zastąp funkcję Dispose () klienta bez konieczności generowania klasy proxy na podstawie ClientBase, a także bez konieczności zarządzania tworzeniem kanałów i buforowaniem ! (Uwaga: WcfClient nie jest klasą ABSTRACT i jest oparty na ClientBase)
źródło
Moją metodą tego było stworzenie odziedziczonej klasy, która jawnie implementuje IDisposable. Jest to przydatne dla osób, które używają GUI, aby dodać odwołanie do usługi (Dodaj odwołanie do usługi). Po prostu upuszczam tę klasę w projekcie, odwołując się do usługi i używam jej zamiast domyślnego klienta:
Uwaga: To tylko prosta implementacja usuwania, możesz zaimplementować bardziej złożoną logikę usuwania.
Następnie możesz zastąpić wszystkie połączenia wykonane zwykłym klientem usługi bezpiecznymi klientami:
Podoba mi się to rozwiązanie, ponieważ nie wymaga ode mnie dostępu do definicji interfejsu i mogę używać
using
instrukcji zgodnie z oczekiwaniami, jednocześnie pozwalając, aby mój kod wyglądał mniej więcej tak samo.Nadal będziesz musiał obsługiwać wyjątki, które mogą zostać zgłoszone, jak wskazano w innych komentarzach w tym wątku.
źródło
Możesz także użyć a,
DynamicProxy
aby rozszerzyćDispose()
metodę. W ten sposób możesz zrobić coś takiego:źródło