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ą:
- Tworzymy nowy projekt aplikacji klienckiej np. WPF lub WindowsForms.
- Dodajemy referencję do usługi za pomocą Add Service Reference (menu kontekstowe).
- 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:).
Wygląda to całkiem ciekawie, nie trzeba pisać dziesiątek metod webserwisowych CRUD, a mamy w prosty sposób wydzieloną warstwę zarówno logiczą jak i fizyczną. Brak joina sporą wadą… trzeba klepać widoki w SQL’u. Czy WCF Data Service możemy dopisać własną metodę, tak aby w przypadku zapytań wymagających złączeń “zaszyć w niej join’a”?
Tak. Nie musisz wcale pisać widoków w SQL tylko właśnie dopisać metodę do WCF Data Service(to wciąż jest zwykły WCF tak naprawdę). Np:
[WebGet()] GetProductsByBarCode(string barCode)
public IEnumerable
{
return CurrentDataSource.Products.Where((product) => product.BarCode == barCode);
}
I wtedy URL wywołania to:
http://localhost:5744/Services/SalesSystemService.svc/GetProductsByBarCode?barCode='4242‘
Ponadto metodom można nadawać dostęp(READ,WRITE itp.)
Czy transmisję danych pomiędzy WCF Data Service a klientem można zaszyfrować? Udało mi się uruchomić serwis dostępny tylko poprzez HTTPS, ale w jaki sposób poprawić klienta aby mógł z niego korzystać?
ok, już wiem. Trzeba obsłużyć walidację certyfikatu:
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customCertificateValidation);
private static bool customCertificateValidation(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
//analyze the certificate and then return true.
return true;
}