SOAP SL4Prawie każda aplikacja nawet Silverlight potrzebuje zbioru danych aby osiągnąć swój cel. Silverlight podobnie jak WPF oferuje możliwość bindowania danych jak i efektownego wyświetlania. Jednak skąd biorą się te dane. Ponieważ jak wcześniej ustaliliśmy Silverlight działa po stronie klienta. Znajduje się ona jakby po drugiej stronie muru gdzie nie ma bezpośredniego dostępu do danych, jak to jest w ASP.NET.

Aby mieć dostęp do danych wewnątrz Silverlight-a trzeba użyć usługi sieciowej, która przejdzie dla nas przez ten mur. Większość usług sieciowych wpada w dwie kategorie SOAP i REST. Nie spieszmy się z tym mamy trochę czasu więc po kolei wyjaśnię co te terminy oznaczają.

Używanie usługi SOAP

Krótko mówiąc ponieważ o SOAP można długo opowiadać, kiedyś ktoś zdał sobie sprawę, że różne aplikacje działają na różnych bazach danych i z powodu tej niekompatybilności dane nie są w ogólnie dostępne. W tamtym okresie nawet stworzenie usług sieciowych nie rozwiązywało problemu, bo były one dostępne tylko dla jednej technologii. Ktoś powiedział dosyć i stworzył jeden standard, który ma działać zawsze i wszędzie. Kiedy myślisz o klasycznej usłudze sieciowej myślisz o “mydle” xD . Usługi SOAP śledzą określone protokoły, które definiują format, w których wiadomości są wysłane w tą i z powrotem.

Silverlight dobrze wspiera usługi sieciowe tego rodzaju. Wspiera WS-I Basic Profile 1.0 (SOAP 1.1) oraz SOAP 1.2 i WS-Addressing 1.0 jak i też niewielką zawartość WS-Security.

Usługi SOAP to potężne narzędzia wraz z prostą implementacją poprzez proste dodanie referencji usługi… Dosyć tego zachwalania przejdźmy do rzeczy i stwórzmy pierwszą aplikacje SOAP.

SOAP
Alternate Text Skrót SOAP początkowo oznaczał “Simple Object Access Protocol” jednak ta definicja została oficjalnie odrzucona w wersji 1.2 przez W3C SOAP standard.

Prosta Aplikacja SOAP – Tworzenie usługi

Na początek stwórzmy usługę sieciową SOAP. Mając gotowy projekt Silverlight wraz z projektem Web, kliknij na projekt web prawym przyciskiem myszki i w meni wybierz “Add new Item”.

SOAP

Po stworzeniu usługi dodałem jakieś dwie banalne metody w jej ciele. Chociaż też chciałem udowodnić , że usługi mogą nie tylko pobierać coś z baz danych , ale też wykonywać inne czynności niemożliwe od strony klienta. Jak choćby pobranie nazwy plików w katalogu, gdzie znajduje się usługa. Silverlight nawet nie ma klasy DiretoryInfo (zresztą po co xD).

namespace SilverSOAP.Web.Service
{
    [WebService(Namespace = "http://Service.CezarWalenciuk.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    public class CesarWebService : System.Web.Services.WebService {
        [WebMethod]
        public string PolishNumber(int number)
        {
            switch (number)
            {
                case 0: return "zero";
                case 1: return "jeden";
                default: return "koniec";
            }
        }
        [WebMethod]
        public List<string> NumberOfFiles()
        {
            var path = Server.MapPath("");
            DirectoryInfo d = new DirectoryInfo(path);
            List<string> lista = new List<string>();
            foreach (var item in d.GetFiles())
            {
                lista.Add(item.Name);
            } 
            return lista;
        }
    }
}

Każdy usługa ma swoją przestrzeń nazw w tym wypadku “http://Service.CezarWalenciuk.org/”. Nie musi to być jakiś aktualny egzystujący adres po prostu warto o to zadbać aby przestrzenie nazw nie powielały się w usługach. By sprawdzić czy usługa działa poprawnie - można ją obejrzeć klikając prawy przyciskiem i wybierając w meni “View in a Browser”. Jeśli nasza metoda nie ma parametrów bądź potrzebne parametry to zwykłe int-y oraz stringi można ją przetestować bez problemów.

usługa sieciowa w metodzie PolishNumberUsługa CesarWebService oraz z próbą testowania metody PolishNumber

Powinieneś zwrócić uwagę, że SOAP zwraca wszystko w postaci pliku XML (i chyba w formacie POX) ma to sens ponieważ usługa ma przesłać wartości do każdego odbiorcy, nie tylko do czegokolwiek w .NET. Przejdź dalej i zobaczymy co na to Silverlight.

Dodanie usługi w Silverlight

Dodanie usługi sieciowej a raczej jej referencji nie wymaga dużego wysiłku. Klikamy prawym przyciskiem myszki i z menu wybieramy “Add Service Reference”. W oknie dialogowym klikamy na przycisk “discovery”aby ułatwić sobie życie, po czym powinnyśmy odnaleźć naszą usługę sieciową.

Po dodaniu referencji do aplikacji Silverlight powinny być dodane biblioteki “System.SerivceModel” i “System.Runtime.Serialization”. Dzięki nim Silverlight może się połączyć z usługą i serializować je oraz deserializować.

Pamiętaj
Alternate Text Za każdym razem gdy zmieniamy coś w naszej usłudze sieciowej trzeba w naszej aplikacji ją zaktualizować . Robi się to poprzez prawe kliknięcie na referencje oraz na wybór jej zaktualizowania w menu. Gdy to nie pomoże możemy przejść do jej skonfigurowania. Zwłaszcza jeśli zmieniliśmy adres naszej usługi.

W aplikacji standardowo dodałem przycisk i jakiś TextBlock, który ma wyświetlić rezultat zapytania. Następujący fragmentu kodu wymaga jednak dużego objaśnienia.

private void Button_Files(object sender, RoutedEventArgs e)
{
          Binding myBinding = new BasicHttpBinding();
          EndpointAddress myEndpoint = new EndpointAddress(
          "http://localhost:4000/Service/CesarWebService.asmx");

          CesarServiceReference.CesarWebServiceSoapClient Proxy =
              new CesarServiceReference.CesarWebServiceSoapClient (myBinding,myEndpoint);

          Proxy.NumberOfFilesCompleted += new EventHandler <CesarServiceReference.NumberOfFilesCompletedEventArgs>
              (NumberOfFilesCompleted);
          Proxy.NumberOfFilesAsync();
}

void NumberOfFilesCompleted(object sender, CesarServiceReference.NumberOfFilesCompletedEventArgs e)
{
          foreach (var item in e.Result)
          {
            TB_2.Text += item + "\n";
          }

          (sender as CesarServiceReference.CesarWebServiceSoapClient).CloseAsync();   
}

Kod pokazuje cały proces tworzenia proxy poprzez dodanie zdarzenia do wywołania asynchronicznej metody a w niej odpowiedniego obsłużenia jej rezultatu. W tym przykładzie usługa SOAP, do której się podłączamy wykonuje metodę “NumberOfFiles”, która nie potrzebuje żadnych właściwości do podania.

Na początku tworzymy powiązanie (inne niż te, o których zazwyczaj rozmawiamy) typu “BasicHttpBinding”. Mówi to proxy, że usługa, do której się podłączamy korzysta z SOAP 1.1 . Podstawowy konstruktor dla “BasicHttpBinding” tworzy powiązanie bez żadnych zabezpieczeń. Opcjonalny parametr w konstruktorze akceptuje obiekt “BasicHttpSecurityMode” , który pozwala powiedzieć w jaki sposób ma być zabezpieczona usługa; np. żeby używała SSL w czasie transmisji danych. Tworzysz także “EndpointAdress” , która opisuje URI gdzie usługa SOAP się wywołuje.

Pamiętaj
Alternate Text Po raz kolejny trzeba ustawić określony port dla naszej strony. Ponieważ on się zmienia nasza usługa może być niedostępna.

Następnie musisz stworzyć wydarzenie, które się wywoła po zakończeniu wywołania asynchronicznego. Możesz to zrobić w Visual Studio poprzez naciśniecie przycisku “+” a potem “=” , a potem po naciśnięciu dwa razy tabulatora, po wybraniu swojego zdarzenia w proxy. Teraz uruchom metodę “<coś>Asynch”() by wysłać zapytanie .

W metodzie obsługującej wynik naszego zapytania trzeba jeszcze zamknąć nasze połączenie asynchroniczne. Garbage collection technicznie zamknie każde stare połączenie ale dobrą praktyką programistyczną jest zamknięcie każdego połączenia którego się używa.

Jeśli chodzi o metodę “PolishNumbers” wykonuje się ona w ten sam sposób tylko trzeba dodać do niej jakiś parametr liczbowy.

Użycie bardziej skomplikowanych danych

Wysłanie i otrzymywanie bardziej złożonych informacji przez SOAP też jest również łatwą sprawą. Kiedy tworzysz referencje usługi, obiekty używane przez wiadomości SOAP są automatycznie analizowane i od strony klienta proxy je też podsiada. Z tym, że proxy może tworzyć własne instancje obiektów w swojej aplikacji.

[WebMethod]
public void AddNewAmigaGame(AmigaGame Game)
{
    //dodanie do bazy danych nowego rekordu
}

public class AmigaGame
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int NumberOfDisks { get; set; }
}

Kod pokazuje jak złożone typy danych mogą być wysyłane przez usługi wewnątrz aplikacji Silverlight. Przykładowo możemy w ten sposób stworzyć banalne metody do kasowania, dodawania, edytowania rekordów z baz danych mimo, iż nie mamy bezpośredniego dostępu do bazy. Kod po stronie Silverlight:

CesarServiceReference.AmigaGame Game = new CesarServiceReference.AmigaGame 
{
        Id = 1,Name = "Mortal Kombat",NumberOfDisks=3;
}
Proxy.AddNewAmigaGameAsync(Game);

Jednak jest to tylko przykład do takiego używania SOAP. Trzeba jeszcze to jakoś zabezpieczyć aby nie każdy mógł wywoływać takie metody. Na razie nagłówki SOAP zostawię w spokoju bo czytałem, że Silverlight średnio do tego się nadaje. Prawdopodobnie dlatego, że WCF jest bardziej wspieranym rozwiązaniem, co mnie zresztą nie dziwi.

Użycie pliku konfiguracyjnego

Jak do tej pory wywoływaliśmy usługę podając wszystkie informacje w kodzie. Silverlight daje możliwość innego obsłużenia tych informacji . Umieszczając je jednorazowo w pliku konfiguracyjnym “ServerReferences.ClientConfig”.

Plik konfiguracyjny w XAP
Alternate TextServiceReferences.ClientConfig jest to plik XML , który jest tworzony automatycznie gdy referencja usługi jest dodana. Plik ten jest spakowany w pliku .xap , który też przechowuje twoją aplikacje . Plik pomimo rozszerzenia XAP może być otworzony jak zwykły plik zip. Zedytuj plik i spakuj plik ponownie.

Przykład pliku konfiguracyjnego jest pokazany poniżej. Plik konfiguracyjny ustala typ kodowania, maksymalny rozmiar wiadomości, kontrakt wiążujący oraz punkt końcowy.

<configuration>
<system.serviceModel>
  <bindings>
     <customBinding>
        <binding name="CesarWebServiceSoap">
            <binaryMessageEncoding />
            <httpTransport maxReceivedMessageSize="2147483647"
                  maxBufferSize="2147483647">
            </httpTransport>
        </binding>
     </customBinding>
   </bindings>
   <client>
     <endpoint address="http://localhost:4000/Service/CesarWebService.asmx"
        binding="customBinding" 
 bindingConfiguration="CesarWebServiceSoap"
        contract="CesarServiceReference.CesarWebServiceSoap"
 name="CesarWebServiceSoap" />
   </client>
</system.serviceModel>
</configuration>

Twój plik konfiguracyjny może wyglądać inaczej, ja wprowadziłem kilka poprawek. Ten przykład używa nowego bineralnego kodowania. To redukuje rozmiar wiadomości nawet w sytuacjach gdzie serwer jak i klient nie używają kompresji GZIP , a serwer nie chodzi na .NET 3.5 lub wyżej. W dodatku serwer lepiej sobie radzi z naturą binarną wiadomości. Jednak utrudnia to życie dla innych klientów.

Pliki konfiguracyjne ułatwiają życie jednak trzeba pamiętać o tym aby je zmienić gdy migrujemy na inny serwer. Za pomocą kodu możesz pobrać adres danego serwera i bazując na tym wywołać usługę. Ale nawet te rozwiązanie może się rozsypać jeśli zmieniłeś folder gdzie znajduje się usługa.

Teraz nasz kod może być krótszy. By nie powielać kodu, pokażę jak bardziej efektownie podpiąć zdarzenie kończące pobieranie za pomocą wyrażenia lambda. Zaletą takiego rozwiązania jest przejrzystość kodu. Pamiętaj jednak, że to nie sprawia, że kod nagle stał się synchroniczny, po prostu w inny sposób stworzyliśmy zdarzenie. Dodałem też do kodu using (using SilverSOAP.CesarServiceReference;) aby nie pisać po raz “n-ty” tej przestrzeni nazw.

private void Button_Short(object sender, RoutedEventArgs e)
{
           var S = new CesarWebServiceSoapClient();
           S.NumberOfFilesCompleted += (ss, ee) =>
           {
               TB_2.Text = ee.Result.First();
           };
           S.NumberOfFilesAsync();
}

To byłoby na tyle jeśli chodzi o użycie SOAP w Silverlight. Oczywiście Silverlight nie ogranicza się tylko do usługi SOAP. W następnym rozdziale stworzymy z automatu prostą usługę REST i użyjemy Silevrlight-a aby ją odczytać.

Pobierz Kod