Programowanie Systemów Rozproszonych Laboratorium 2 WCF Paweł Paduch paduch@tu.kielce.pl 21-03-2016
Rozdział 1 Wstęp 1.1 Materiały pomocniczne Do zajęć przydatne mogą być: materiały udostępniane przez prowadzących wykłady książki związane z tematyką programowania np. Język C# 2010 i platforma.net Andrew Troelsena książki związane z tematyką programowania współbieżnego np. Programowanie równoległe i asynchroniczne w C# 5.0 kursy i poradniki w sieci np. http://www.centrumxp.pl/ 1.2 Definicje WCF - Windows Communication Foundation. System integrujący wszystkie microsoftowe dotychczasowe technologie służące komunikacji Serwis - udostępnia jeden lub więcej Endpointów Endpoint - udostępnia jeden lub więcej Service Operation. Określa adres gdzie może być znaleziony serwis,binding oraz kontrakty Binding - określa jak klient ma się komunikować z serwisem kontrakty - definiują funkcjonalność dostarczoną przez serwisy do klientów. 1
Rozdział 2 Przykładowy Web Serwis Po zalogowaniu się z menu start odnajdujemy program Microsoft Visual Studio 2010, lub nowszy. 2.1 Service Contract Z górnego menu wybieramy: File-> New Project. W okienku jak na rysunku Rysunek 2.1: Okienko wybierania nowego projektu 2.1 zaznaczamy Visual C#->Windows WCF Service Library. Na dole wpisujemy nazwę projektu WcfServiceLibrary i nazwę solucji Lab2, potwierdzamy OK. Powinny powstać nam 3 pliki: IService1.cs Service.cs App.config 2
Zmieniamy nazwę IService1.cs na ICalculator.cs. W interfejsie definiujemy jakie operacje będzie udostępniać nasz serwis. 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.Serialization; 5 using System.ServiceModel; 6 using System.Text; 7 Listing 2.1: ICalculator.cs 8 namespace WcfServiceLibrary 9 { 10 [ServiceContract(Namespace="http://achilles.tu.kielce.pl")] 11 public interface ICalculator 12 { 13 [OperationContract] 14 double Add(double n1, double n2); 15 [OperationContract] 16 double Subtract(double n1, double n2); 17 [OperationContract] 18 double Multiply(double n1, double n2); 19 [OperationContract] 20 double Divide(double n1, double n2); 21 } 22 } 2.2 Implementacja Kontraktu Zmieniamy nazwę pliku Service1.cs na Calculator.cs Ustawiamy dziedziczenie CalculatorService po ICalculator. Można posłużyć się rozwijanym menu by wygenerować automatycznie odpowiednie metody, jak na rysunku 2.2 Rysunek 2.2: Implementacja interfejsu Listing 2.2: Zaimplementowane metody w CalculatorService.cs 1 public class CalculatorService : ICalculator 2 { 3
3 public double Add(double n1, double n2) 4 { 5 double result = n1 + n2; 6 Console.WriteLine("Dodawanie liczb: Add({0},{1})", n1, n2); 7 Console.WriteLine("Wynik: {0}", result); 8 return result; 9 } 10 11 public double Subtract(double n1, double n2) 12 { 13 double result = n1 - n2; 14 Console.WriteLine("Odejmowanie liczb: Subtract({0},{1})", n1, n2); 15 Console.WriteLine("Wynik: {0}", result); 16 return result; 17 } 18 19 public double Multiply(double n1, double n2) 20 { 21 double result = n1 * n2; 22 Console.WriteLine("Mnożenie liczba: Multiply({0},{1})", n1, n2); 23 Console.WriteLine("Wynik: {0}", result); 24 return result; 25 } 26 27 public double Divide(double n1, double n2) 28 { 29 double result = n1 / n2; 30 Console.WriteLine("Dzielenie liczb: Divide({0},{1})", n1, n2); 31 Console.WriteLine("Wynik: {0}", result); 32 return result; 33 } 34 } 2.3 App.config Należy, zmienić nazewnictwo w pliku App.config z Service1 na CalculatorService oraz IService1 na ICalculator. 1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 Listing 2.3: App.config 4 <system.web> 5 <compilation debug="true" /> 6 </system.web> 7 <!-- When deploying the service library project, the content of the config file must be added to the host's 8 app.config file. System.Configuration does not support config files for libraries. --> 9 <system.servicemodel> 10 <services> 11 <service name="wcfservicelibrary.calculatorservice"> 12 <host> 13 <baseaddresses> 14 <add baseaddress = "http://localhost:8733 /Design_Time_Addresses/WcfServiceLibrary/CalculatorService/" /> 15 </baseaddresses> 16 </host> 17 <!-- Service Endpoints --> 18 <!-- Unless fully qualified, address is relative to base address supplied above --> 19 <endpoint address="" binding="basichttpbinding" contract=" WcfServiceLibrary.ICalculator"> 20 <!-- 21 Upon deployment, the following identity element should be removed or replaced to reflect the 4
22 identity under which the deployed service runs. If removed, WCF will infer an appropriate identity 23 automatically. 24 --> 25 <identity> 26 <dns value="localhost"/> 27 </identity> 28 </endpoint> 29 <!-- Metadata Endpoints --> 30 <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. --> 31 <!-- This endpoint does not use a secure binding and should be secured or removed before deployment --> 32 <endpoint address="mex" binding="mexhttpbinding" contract="imetadataexchange" /> 33 </service> 34 </services> 35 <behaviors> 36 <servicebehaviors> 37 <behavior> 38 <!-- To avoid disclosing metadata information, 39 set the value below to false before deployment --> 40 <servicemetadata httpgetenabled="true"/> 41 <!-- To receive exception details in faults for debugging purposes, 42 set the value below to true. Set to false before deployment 43 to avoid disclosing exception information --> 44 <servicedebug includeexceptiondetailinfaults="false" /> 45 </behavior> 46 </servicebehaviors> 47 </behaviors> 48 </system.servicemodel> 49 50 </configuration> 2.4 Pierwsze uruchomienie Już teraz można uruchomić nasz serwis wciskając F5. Serwis będzie hostowany w środowisku uruchomieniowym, włączony zostanie też prosty klient do testowania rozwiązań (rys. 2.3). Rysunek 2.3: WCF Test Client 5
2.5 Hostowanie serwisu Stworzymy sobie prostą aplikację konsolową hostującą nasz serwis. 1. Dodaj do solucji nowy projekt typu Console Application i nazwij ją CalculatorHost. 2. We właściwościach projektu zmień Target framework na.net Framework 4.5 (rys. 2.4). Projekt zostanie przeładowany. 3. Dodaj referencję do WcfServiceLibrary. W solution explorerze prawym klawiszem myszy na klikamy na References i wybieramy Add reference. Wybieramy z menu z lewej strony Solution -> Projects i zaznaczamy WcfServiceLibrary (rys. 2.5). Dzięki temu aplikacja będzie znała typy używane w projekcie serwisu. 4. Dodaj referencję do System.ServiceModel. Podobnie jak wyżej, tylko należy wybrać nie Solution a Assemblies->Framework i tam zaznaczyć System.ServiceModel. Rysunek 2.4: Właściwości projektu Uzupełnij kod w pliku Program.cs jak na listingu 2.4 Listing 2.4: Główny program hostujący serwis 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using WcfServiceLibrary; 6
Rysunek 2.5: Dodanie referencji do serwisu 7 using System.ServiceModel.Description; 8 9 10 namespace CalculatorHost 11 { 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 // Tworzymy adres gdzie pod którym będzie dostępna usługa 17 Uri baseaddress = new Uri("http://localhost:8000/CalculatorService/"); 18 19 // Tworzymy obiekt klasy CalculatorService 20 ServiceHost selfhost = new ServiceHost(typeof(CalculatorService), baseaddress); 21 22 try 23 { 24 // Dodajemy Endopoint usługi 25 selfhost.addserviceendpoint(typeof(icalculator), new WSHttpBinding(), "CalculatorService"); 26 27 // Umożliwiamy wymianę metadanych 28 ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); 29 smb.httpgetenabled = true; 30 selfhost.description.behaviors.add(smb); 31 32 // Startujemy serwis 33 selfhost.open(); 34 Console.WriteLine("Serwis działa..."); 35 Console.WriteLine("Naciśnij <ENTER> by zakończyć."); 36 Console.WriteLine(); 37 Console.ReadLine(); 38 39 // zamykamy serwis 40 selfhost.close(); 41 } 42 catch (CommunicationException ce) 43 { 44 Console.WriteLine("Przechwyciłem wyjątek: {0}", ce.message); 45 selfhost.abort(); 46 } 47 } 48 } 49 } 7
W linii 25 tworzymy Endpoint serwisu. Jest on złożony z adresu, bindingu oraz kontraktów. Kontrakt to ICalculator. Binding to WSHttpBinding, który jest predefiniowanym bindingiem. Od wersji.net 4.0 podawanie bindingu nie jest wymagane. Zostanie on automatycznie wygenerowany dla wszystkich par bazowy adres i kontrakt. W linii 28 umożliwiamy wymianę metadanych serwisu. Klienci używają metadanych do wygenerowania proxy służących do komunikowania się z serwisem. Należy: stworzyć obiekt klasy ServiceMetadataBehavior ustawić jego parametr HttpGetEnabled na true dodać zachowanie (behavior) do kolekcji System.ServiceModel.ServiceHost.Behaviors. W linii 33 otwieramy host do nasłuchiwania przychodzących komunikatów. Ustawiamy projekt CalculatorHost jako StartUp Project i uruchamiamy. Możemy uruchomić przeglądarkę i wejść na adres: http://localhost:8000/calculatorservice/. Powinniśmy zobaczyć linki do plików WSDL oraz przykład prostego klienta. 2.6 Klient Klient korzysta z klas proxy wygenerowanych na podstawie metadanych. Metadane często są w postaci pliku WSDL (plik xmlowy opisujący web service). Klasy proxy można wygenerować za pomocą narzędzia Svcutil.exe dostępnego z konsoli Visual Studio Command Prompt lub z poziomu Visual Studio. 1. Dodaj nowy projekt typu aplikacja konsolowa o nazwie CalculatorClient- Program do solucji Lab2. 2. Ustaw we właściwościach projektu Target Framework na.net 4.5 3. Dodaj referencje do System.ServiceModel 4. Dodaj dodaj referencje do servisu. W tym celu należy: Uruchomić program CalculatorHost tak by serwis działał i wystawił metadane na adresie http://localhost:8000/calculatorservice/. Należy uruchomić w trybie bez debugowania (ctrl+f5). Prawym klawiszem myszy kliknąć w solution explorerze na References w projekcie CalculatorClientProgram i wybrać Add Service Reference. podać adres serwisu i nacisnąć guzik Go. po odkryciu naszego serwisu podajemy nazwę przestrzeni nazw CalculatorServiceReference (rys. 2.7) i naciskamy OK. zostanie wygenerowany kod do klas proxy oraz plik konfiguracyjny app.config. 8
Rysunek 2.6: Serwis w przeglądarce 9
Rysunek 2.7: Dodaj referencje do serwisu 5. Jeżeli chcielibyśmy użyc narzędzia Svcutil.exe należałoby podać mniej więcej taką komendę: svcutil.exe /language:cs /out:generatedproxy.cs /config:app.config http://localhost:8000/servicemodelsamples/service 6. Uzupełniamy kod programu o odpowiednią przestrzeń nazw oraz tworzymy obiekt klasy CalculatorClient wołamy prostą metodę client.add jak listeningu (2.5) Listing 2.5: Calculator Client 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using CalculatorClientProgram.CalculatorServiceReference; 6 7 namespace CalculatorClientProgram 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //krok 1: Tworzymy obiekt klasy proxy 14 CalculatorClient client = new CalculatorClient(); 15 16 // Krok 2: Zawołaj metodę serwisu 17 // Dodawanie 18 double value1 = 100.00D; 19 double value2 = 15.99D; 20 double result = client.add(value1, value2); 21 Console.WriteLine("{0} + {1} = {2}", value1, value2, result); 22 23 /// Tu dodaj kolejne wywołania metod 24 10
25 26 // Krok 3: zamknij połączenie i wyczyść 27 client.close(); 28 Console.ReadLine(); 29 } 30 } 31 } 11
Rozdział 3 Zadania do samodzielnego wykonania 1. W programie klienta przetestować pozostałe 3 metody. (1pkt.) 2. Dodać do WcfServiceLibrary metodę liczącą potęgę X y. Odświeżyć referencje CalculatorServiceReference. Zademonstrować działanie nowej metody w programie klienta. (1pkt.) 3. Dodać do WcfServiceLibrary metodę liczącą sumę wszystkich elementów listy List<double> (przy właściwościach CalculatorServiceReference należy zmienić Collection type na System.Collections.Generic.List albo wykonać konwersję listy double do tablicy double. Zaprezentować nową metodę w programie klienckim. (1pkt.) 4. Stwórz nową usługę, w której będzie można wykonać następujące operacje: Podaj ile liter ma łańcuch znakowy Połącz teksty Posortuj tablicę liczb typu int Usługa powinna być hostowana w programie konsolowym. Demonstracja działania w osobnym programie klienckim. (2pkt.) 12