WCF Data Services – aplikacja kliencka

W poprzednim poście przedstawiłem w skrócie czym jest WCF Data Service i jak stworzyć prostą usługę sieciową opartą o WCF Data Service. Dzisiaj przyjrzymy się jak stworzyć aplikacje kliencką za pomocą bibliotek dołączonych do .NET.

.NET ułatwia znacząco dostęp do usług WCF Data Service. Programista nie musi samemu tworzyć zapytań URL. Korzystanie z WCF Data Service bardzo przypomina pracę z lokalnym EntityFramework. Stwórzmy więc aplikację kliencką:

  1. Tworzymy nowy projekt aplikacji klienckiej np. WPF lub WindowsForms.
  2. Dodajemy referencję do usługi za pomocą Add Service Reference (menu kontekstowe).
  3. W zasadzie już wszystko jest gotowe! Teraz wystarczy tylko wywoływać odpowiednie metody w celu np. selekcji danych.

Jak już wspomniałem korzystanie z WCF Data Service przypomina prace ze zwykłym, lokalnym EntityFramework. Należy jednak pamiętać, że WCF Data Service to zwykła usługa sieciowa. Utworzenie instancji klienta usługi WCF Data Service różni się tym od zwykłego WCF, że należy przekazać adres usługi w konstruktorze:

Sales.DataServicesEntities context = new new Sales.DataServicesEntities(serviceUri);

W przypadku inicjalizowania klienta zwykłej usługi wystarczyłby wyłącznie konstruktor bezparametrowy. Spróbujmy zatem zwrócić listę wszystkich encji Product:

dataGrid1.ItemsSource = m_Context.Products;

W celu znalezienia encji o wskazanym ID wystarczy:

this.m_Context.Products.Where((product) => product.BarCode == productSearch.BarCode)

Sortowanie danych:

m_Context.Products.OrderBy((product) => product.BasePrice);

Wstawianie encji:

m_DataContext.AddToProducts(ProductModel);

Usuwanie encji:

m_DataContext.DeleteObject(ProductModel);

Aktualizacja obiektu:

m_Context.UpdateObject(Product)

Warto jeszcze raz podkreślić, że WCF Data Service korzysta z protokołu HTTP. Dzięki temu bardzo łatwo wprowadzić buforowanie (HTTP caching).

Jak widać, z WCF Data Service można korzystać za pomocą języka LINQ. Niestety niektóre konstrukcje nie są wspierane. Poniższe zapytanie LINQ (join) nie zostanie prawidłowo przetłumaczone na URL:

            var invoices = from orderItem in m_Context.OrderItems
                    join invoice in m_Context.Invoices on
                        orderItem.ID_INVOICE equals invoice.ID_INVOICE
                    select invoice;

Jedynym sposobem na złączenie encji jest wykorzystanie metody expand oraz zdefiniowanych na serwerze relacji:

IEnumerable<Sales.Invoice> invoices = m_Context.Invoices.Expand("OrderItems");

Należy podkreślić, że expand jest bardzo niewydajne ponieważ złączenie występuje lokalnie w pamięci a nie w silniku baz danych. W przyszłości postaram się zamieścić cały artykuł o WCF Data Service – to co w tych dwóch postach przedstawiłem to tylko próbka możliwości WCF Data Service:).

WCF Data Services

WCF Data Service to usługa sieciowa umożliwiająca łatwy dostęp do danych. Wyobraźmy sobie następujący przypadek:

image

Mamy pewną bazę danych zawierającą np. informacje o produktach. Można napisać ręcznie usługę WCF, która wyeksponuje wszelkie potrzebne dane za pomocą metod. Usługa w takim przypadku zawierałaby metody typu Create, Update, Delete, GetById, GetByQuery itp. Implementacja usługi dla każdej tabeli w bazie jest dość czasochłonna i niezbyt interesująca. Za pomocą WCF Data Service, usługa zostanie stworzona automatycznie na podstawie modelu encji (np. Entity Framework). Ponadto komunikacja między klientem a WCF może odbywać się za pomocą zapytań LINQ, tak jakbyśmy korzystali z lokalnego Entity Framework.

WCF Data Service oparty jest o architekturę REST (tzw. RESTful Service). Architektura REST składa się z serwera oraz klienta. Klient wysyła żądanie do serwera, które następnie jest przetwarzane. Serwer ma dostęp do zasobów i zwraca tzw. reprezentację zasobów (representation of resource), która przeważnie przyjmuje postać dokumentu XML. Zasoby muszą mieć jakiś jednoznaczny identyfikator (np. URI). Serwer nie przechowuje informacji o stanie – to klient odpowiedzialny jest za przejścia między różnymi stanami.

Architektura REST musi spełniać następujące warunki:

•    Bezstanowość ,
•    Buforowanie danych,
•    SoC  (separation of concerns),
•    Budowa warstwowa,
•    Ujednolicony protokół komunikacji.

Sama idea architektury jest stara (po raz pierwszy została przestawiona w  pracy doktorskiej Roya T. Fieldinga, 2000). W praktyce protokołem komunikacji między serwerem a klientem jest najczęściej HTTP. W takim przypadku identyfikatorem zasobów jest zwykły URL (http://www.localhost/users). W zależności od użytej metody HTTP, serwer wykona jedną z następujących operacji:

  1. HTTP POST – tworzenie zasobu,
  2. HTTP GET – selekcja danych ,
  3. HTTP PUT – aktualizacja,
  4. HTTP DELETE –  usunięcie danych.

Przykładowo, poniższe zapytanie spowoduje pobranie listy użytkowników z bazy danych:

GET /users HTTP/1.1
Host: localhost
Accept: application/xml

Usługa sieciowa, po otrzymaniu takiego zapytania wywoła metodę odpowiedzialną za selekcję danych z kolekcji encji users. Architektura WCF Data Service wygląda następująco (źródło MSDN):

image

Klient komunikuje się z usługą za pomocą HTTP. Następnie Data Services Runtime wykorzystując np. Entity Framework pobiera dane z bazy. Możliwe jest napisanie własnych provider’ów, które mogą nawet odwoływać się do baz nierelacyjnych.

Myślę, że teorii na początek wystarczy. Więcej informacji o REST znajdziecie np. na wiki :). Skupmy się teraz jak to wygląda od strony praktycznej. Większość operacji wykona za nas IDE:

  1. Na początek należy stworzyć oczywiście projekt WCF Service Application.
  2. Po utworzeniu nowego projektu, dojemy model encji ADO .NET Entity Data Model (Add -> New Item -> Data ->ADO .NET Entity Data Model). Kreator poprowadzi nas i np. wygeneruje model encji na podstawie bazy danych .
  3. Następnie dodajemy Data Service (Add -> New Item -> Web -> WCF Data Service).
  4. Zostanie wygenerowany szablon usługi. Należy teraz wstawić w miejsce komentarza prawidłowy model encji. Po zmianach kod powinien wyglądać np. następująco:
    public class SalesSystemService : DataService<DataServicesEntities>
    {
    }

  5. WCF Data Service pozwala zdefiniować prawa dostępu. Można umożliwić klientom np. dostęp tylko do odczytu do produktów:

    public static void InitializeService(DataServiceConfiguration config)
    {                                    
        config.SetEntitySetAccessRule("Products", EntitySetRights.AllRead);            
    }
    

W zasadzie więcej konfiguracji nie trzeba! Wystarczyło stworzyć model encji, DataService, podmienić typ generyczny oraz skonfigurować prawa dostępu. W tej chwili możemy odpalić usługę i za pomocą przeglądarki przetestować jej działanie.

Aby uzyskać listę wszystkich produktów wystarczy w przeglądarce wpisać http://localhost:5744/Services/SalesSystemService.svc/Products. Jeśli chcemy aby został zwrócony tylko pojedynczy produkt o wskazanym kluczu głównym wystarczy wpisać http://localhost:5744/Services/SalesSystemService.svc/Products(4). Do dyspozycji jest naprawdę wiele sposobów selekcji. Przykładowo http://localhost:5744/Services/SalesSystemService.svc/Products?$filter=BarCode eq ’55’  zwróci produkty tylko o kodzie kreskowym równym 55.

Wszystkie możliwe zapytania są tematem na osobny post. Microsoft dostarcza biblioteki ułatwiające komunikacje z WCF Data Service. Użytkownik (programista) nie musi ręcznie pisać adresów URL. Może skorzystać z API oraz języka LINQ. Jak to wygląda po stronie klienta przedstawię już w następnym poście.