czwartek, 31 października 2013

[WCF] Clients

Stronę kliencką po utworzeniu serwisów WCF można stworzyć na dwa sposoby. Strona serwerowa wystawia metadane pod specjalnym adresem MEX (Metadata EXchange) lub plik WSDL. Na podstawie tych danych możemy za pomocą narzędzia svcutil wygenerować kod kliencki oraz plik z ustawieniami. Narzędzie svcutil.exe wymaga korzystania z command line, dlatego Microsoft od VS 2008 wprowadził funkcjonalność AddServiceReference, która na podstawie podanego adresu z metadanymi tworzy kod w C#, konfigurację oraz dodaje do projektu klienckiego odpowiednie dll-ki. Wygenerowany kod można swobodnie przegenerowywać zmieniając niektóre opcje (klikamy prawym przyciskiem myszy na referencji i wybieramy Update Service Reference).




W oknie możemy wybrać między innymi, czy chcemy generować asynchroniczne wywołania operacji z Operation Contract-ów. Ponadto możemy wybrać, na jaki tym mają być rzutowane kolekcje (lista, tablica itd.). Co więcej, w przypadku, gdy strona kliencka i serwerowa znajdują się w jednej solucji, możemy reużywać niektóre typy zdefiniowane po stronie serwerowej.

Serwisy możemy wywoływać tworząc jawnie channele za pomocą klasy ChannelFactory<T> lub korzystając z wygenerowanych klas proxy, które przejmują odpowiedzialność tworzenia channeli.

bool useProxy = new Random().Next()%2 == 0;
IEvaluationService channel = null;

try
{
    if (!useProxy)
    {
        //Transparent proxy
        ChannelFactory<IEvaluationService> factory =
            new ChannelFactory<IEvaluationService>("WSHttpBinding_IEvaluationService");
        channel = factory.CreateChannel();
    }
    else
    {
        //Generated proxy
        channel = new EvaluationServiceClient("WSHttpBinding_IEvaluationService");
    }

    var eval = new Evaluation()
    {
        TimeSent = DateTime.Now,
        Comments = "Nice",
        Submitter = "Johnny"
    };

    channel.SubmitEvaluation(new EvaluationRequest()
    {
        EvaluationBody = eval
    });

    ((IClientChannel)channel).Close();
}
catch (FaultException e)
{
    ((IClientChannel)channel).Abort();
}
catch (CommunicationException e)
{
    ((IClientChannel)channel).Abort();
}
catch (TimeoutException e)
{
    ((IClientChannel)channel).Abort();
}

Ważne aby odpowiednio obsługiwać wyjątki w WCF.FaultException, to propagacja wyjątku powstałego w kodzie po stronie serwerowej. CommunicationException wynika ze złej konfiguracji endpointu po którejś ze stron (np. źle podana nazwa do konstruktora ChannelFactory). TimeoutException zostanie rzucony, jeżeli serwer nie odpowie przez dłużej niż ustalony przez klienta próg.

Podstawowa różnica pomiędzy poleceniami Close i Abort polega na tym, że pierwsze zamyka połączenie w sposób prawidłowy, natomiast drugie warto wykorzystywać, jeżeli chcemy zamknąć połączenie natychmiast. Polecenie Close zostaje wstrzymane do póki nie zakończą się wszystkie operacje, także te asynchroniczne, może ono także rzucić wyjątkiem typu CommunicationException lub TimeoutException.

Nazwa klasy klienckiej zostaje wygenerowana przez konwencję (odcięcie od nazwy interfejsu pierwszej litery "I" oraz dodanie na końcu "Client"). Również przez konwencję tworzone są metody asynchroniczne, do których podajemy callback, który wywoła się po otrzymaniu odpowiedzi.

var client = channel as EvaluationServiceClient;
client.GetEvaluationsCompleted += (e, o) => Console.WriteLine(o.Result.Length);
client.GetEvaluationsAsync();

Metadane można pobierać nie tylko przez VS czy svcutil, ale także w kodzie. Przykład poniżej.

var endpoints = MetadataResolver.Resolve(typeof (IEvaluationService),
                             new EndpointAddress("http://localhost:8732/evals/mex/"));

foreach (var endpoint in endpoints)
    Console.WriteLine(endpoint.Name);

Brak komentarzy:

Prześlij komentarz