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);

poniedziałek, 28 października 2013

[WCF] Address, Binding

Zdefiniowanie serwisu WCF wymaga określenie tzw. ABC, czyli adresu, bindingów i kontraktu, które odpowiadają na pytania gdzie ? jak ? i co ?

Adres definiuje, gdzie serwis jest hostowany. WCF wspiera różne sposoby przesyłania danych, a co za tym idzie różne są rodzaje adresów. Poniższa tabela przedstawia sposoby adresowania w WCF.

Technologia Przykładowy adres Opis
HTTP http://localhost:8001 Najczęściej używany tekstowy sposób przesyłania danych, dane przeważnie w XML lub JSON
HTTPS https://localhost:8001 Bezpieczny sposób przesyłania danych po HTTP. Szyfrowanie za pomocą TLS lub SSL.
TCP Peer network net.tcp://localhost:8002/Service1, net.p2p://localhost/ Bardzo szybki sposób przesyłania danych w formacie binarnym.
IPC (Inter-process communication over named pipes) net.pipe://localhost/PipeService1 Szybki sposób przesyłania danych w obrębie jednej maszyny (współdzielona pamięć).
MSMQ (Microsoft Message Queue) net.msmq://localhost Wiadomości wysyłane przez klienta są kolejkowane. Użyteczny sposób dla scenariuszy, w których istnieje możliwość rozłączenia klienta z serwerem.

Kluczowym elementem architektury WCF jest binding, odpowiadający na pytanie "jak?". Główne obszary, jakie definiuje binding to protokół transportu danych, format wiadomości oraz szczegóły przesyłania wiadomości specyficzne dla danego protokołu. WCF udostępnia szereg protokołów, niemniej jednak zawsze istnieje możliwość zdefiniowania własnego.Poniżej przedstawiono listę bindingów dostarczanych przez WCF. Te, które rozpoczynają się od przedrostka "net" są dedykowane dla technologii .NET, z kolei bindingi rozpoczynające się od "ws" reprezentują ogólnie przyjęte standardy implementowane w wielu technologiach.

Binding Opis
basicHttpBinding Wiadomości przesyłane po Http i zakodowane w Utf-8. Oparte na WS-I profile.
webHttpBinding Serwisy wystawiane jako requesty Http, format XML lub JSON umożliwia tworzenie serwisów w oparciu o wzorzec REST.
wsHttpBinding Korzysta z zaawansowanych profili WS-*, takich jak WS-Security, WS-Transactions, WS-BusinessActivity, etc.
wsDualHttpBinding Korzysta z tych samych profili, co wsHttpBinding ale wspiera komunikację typu duplex.
wsFederationHttpBinding Profile WS-* rozszerzone o federated identity.
netTcpBinding Wiadomości binarne przesyłane po TCP. Wymaga, aby obie strony korzystały z WCF.
netNamedPipeBinding Zoptymalizowany pod kątem komunikacji w obrębie jednej maszyny.
netPeerTcpBinding Komunikacja po TCP z wykorzystaniem technologii P2P.
netMsmqBinding Asynchroniczna komunikacja z wykorzystaniem kolejki komunikatów.

Bindingi można ustawiać w sposób proceduralny...

ServiceHost host = new ServiceHost(typeof(EvaluationService));

host.AddServiceEndpoint(typeof (IEvaluationService), 
    new BasicHttpBinding(), "http://localhost:8080/evals/");

...ale znacznie wygodniej jest robić to w ustawieniach projektu (App.config lub Web.config), gdzie mamy do dyspozycji narzędzie wbudowane w Visual Studio.


W oknie tym możemy ustawić behavior dla określonego typu bindingu zmieniając domyślne parametry. Po zamknięciu okna zmiany zostaną przeniesione do pliku XML z konfiguracją. Na przykład:

<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="MyBindingConfiguration" allowCookies="true" />
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="EvaluationServiceLibrary.EvaluationService">
        <endpoint address="/evals/" binding="wsHttpBinding" bindingConfiguration="MyBindingConfiguration"
          contract="EvaluationServiceLibrary.IEvaluationService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

niedziela, 27 października 2013

[WCF] ServiceContract, DataContract, MessageContract

Kontrakty w WCF można porównać do kontraktów w życiu codziennym. Podpisując jakiś kontrakt obie strony potwierdzają, że są świadome pewnych warunków. W WCF kontrakt definiuje między innymi wystawione serwisy, adresy, rodzaje wiadomości, jakie te serwisy odbierają i zwracają. WCF wykorzystuje  WSDL (Web Services Description Language) oraz XSD do dostarczenie metadanych na temat wystawionych serwisów. Ważne jest, aby kontrakty były niezależne od technologii, dlatego definiuje się je w neutralnym formacie bazującym na XML. Dzięki temu WebSerwisy wystawione na przykład w technologiach Javy mogą być wykorzystywane przez proxy WCF i aplikację napisaną w .NET i na odwrót.

ServiceContract

Definiuje funkcjonalność wystawioną przez serwis WCF. Dobrą praktyką jest oznaczanie tym atrybutem interfejsu .NET tak, aby zapewnić pewną warstwę abstrakcji nad konkretnymi implementacjami. Przykładowo:

[ServiceContract]
public interface IEvaluationService
{
    [OperationContract(IsOneWay = true)]
    void SubmitEvaluation(Evaluation eval);
    [OperationContract]
    List<Evaluation> GetEvaluations();
}

Property IsOneWay oznacza, że dana operacja nie zwraca wyniku. Konkretną implementację interfejsu można konfigurować poprzez atrybut ServiceBehavior.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
    ConcurrencyMode = ConcurrencyMode.Single)]
public class EvaluationService : IEvaluationService
{
    private List<Evaluation> _evals = new List<Evaluation>();

    public void SubmitEvaluation(Evaluation eval)
    {
        _evals.Add(eval);
    }

    public List<Evaluation> GetEvaluations()
    {
        return _evals;
    }
}

InstanceContextMode:  Single - Singleton, PerCall - zawsze nowa instancje, Session - każdy klient dostaje unikalną instancje w obrębie sesji.
ConcurrencyMode - Single - tylko jeden wątek ma dostęp do obiektu, Multiple - dostępny dla wielu wątków, Reentrant - nowe operacje mogą być wykonywane tylko podczas wywoływania innego serwisu.

DataContract

Definiuje typy, które mają być serializowane i przesyłane dalej przez WCF. Domyślny serializator to DataContractSerializer, który sprawdza, które obiekty są oznaczone tym atrybutem. Serializowane będą te propercje lub pola, które są oznaczone atrybutami DataMember.

[DataContract]
public class Evaluation
{
    [DataMember(IsRequired = true)]
    public string Submitted { get; set; }
    [DataMember(Order = 2)]
    public DateTime TimeSent { get; set; }
    [DataMember]
    public string Comments { get; set; }
}

Jeżeli w odebranej wiadomości nie będzie wartości, którą możnaby zdeserializować na property oznaczone z opcją IsRequired, aplikacja zwróci błąd. Poprzez opcję Order możemy ustawiać kolejność elementów w XML-u.

Problem może pojawić się w sytuacji, gdy chcemy wykorzystać mechanizm polimorfizmu. Jeżeli w sygnaturach metod mamy klasy bazowe, natomiast chcemy zwrócić klasę po nich dziedziczącą musimy użyć atrybutu KnownType. Znane typy dziedziczące po klasie bazowej zostaną dodane do kontraktu..

[DataContract]
[KnownType(typeof(Car))]
public abstract class Vehicle
{
    [DataMember]
    public string Name { get; set; }
}

[DataContract]
public class Car : Vehicle
{
    [DataMember]
    public int Year { get; set; }
}

MessageContract

Pozwala na zdefiniowanie, które pola mają znajdować się w nagłówku SOAP, a które w body. Typ oznaczony typ atrybutem przekazujemy do metody oznaczonej atrybutem OperationContract. 

[DataContract]
public class Evaluation
{
    [DataMember(IsRequired = true)]
    public string Submitted { get; set; }
    [DataMember(Order = 2)]
    public DateTime TimeSent { get; set; }
    [DataMember]
    public string Comments { get; set; }
}

[DataContract]
public class CustomHeader
{
   [DataMember]
   public string Username { get; set; }
}

[MessageContract]
public class EvaluationRequest
{
    [MessageHeader]
    public CustomHeader EvaluationHeader { get; set; }

    [MessageBodyMember]
    public Evaluation EvaluationBody { get; set; }
}

Dzięki temu wiadomość SOAP przyjmuje poniższą postać:

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IEvaluationService/SubmitEvaluation</a:Action>
    <h:EvaluationHeader xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://tempuri.org/">
      <Username xmlns="http://schemas.datacontract.org/2004/07/EvaluationServiceLibrary">Joe</Username>
    </h:EvaluationHeader>
  </s:Header>
  <s:Body>
    <EvaluationRequest xmlns="http://tempuri.org/">
      <EvaluationBody xmlns:d4p1="http://schemas.datacontract.org/2004/07/EvaluationServiceLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <d4p1:Comments>Git</d4p1:Comments>
        <d4p1:Submitted>Yes</d4p1:Submitted>
        <d4p1:TimeSent>2013-10-27T12:46:00</d4p1:TimeSent>
      </EvaluationBody>
    </EvaluationRequest>
  </s:Body>
</s:Envelope>

wtorek, 8 października 2013

[WCF] Wprowadzenie

Windows Communication Foundation to technologia, którą świat ujrzał z .NET Framework 3.0 jako odpowiedź na potrzebę budowania rozproszonych systemów, które MS zaczął marketingowo nazywać "Connected Systems". Podczas tworzenia aplikacji rozproszonych ważne jest, aby umożliwić komunikowanie się systemów napisanych w różnych technologiach. Stąd pomysł serwisów jako jednostek udostępniających pewne funkcjonalności poprzez przesyłanie ustandaryzowanych wiadomości.

Dominują dwa wzorce, SOAP i REST.

SOAP (Simple Object Access Protocol) - wiadomości wysyłane są w formacie XML i WS-* protocols. Wspiera wiele protokołów transportowych np. HTTP, TCP, MSMQ.

REST (Representational State Transfer) - różne formaty wiadomości (XML, coraz częściej JSON), protokół transportowy HTTP, paradygmat ukierunkowany na adresowanie, modyfikowanie i odczytywanie zasobów.

WCF wprowadza unifikację, umożliwiając wymianę wiadomości po różnych protokołach, w różnych formatach, wzorcach.

Większość funkcjonalności WCF zawarte jest w System.ServiceModel.dll. Najprościej filozofię WCF oddaje zdanie: serwisy wystawiają swoje funkcjonalności poprzez endpointy, natomiast klienci konsumują je poprzez kanały. WCF-owe endpointy składają się z trzech składowych ABC definiujące podstawowe pojęcia (Address - gdzie?, Binding - jak?, Contract - co?).

Tworzenie najprostszej aplikacji WCF:

Pierwszy projekt to serwisy WCF.

[DataContract]
public class Name
{
    [DataMember]
    public string First { get; set; }

    [DataMember]
    public string Last { get; set; }
}

[ServiceContract]
public interface IMyFirstService
{
    [OperationContract]
    string SayHello(Name name);
}

public class MyFirstService : IMyFirstService
{
    public string SayHello(Name name)
    {
        return string.Format("Hello, {0} {1}", name.First, name.Last);
    }
}

Klasa Name będzie przesyłaną wiadomością, natomiast oznaczenie interfejsu przez atrybuty definiuje, jakie operacje chcemy wystawić na zewnątrz. Kolejne zmiany nanosimy w pliku App.config.Wystawiamy jeden serwis dostępny pod trzema endpointami wykorzystującymi różne protokoły transportowe.

<services>
  <service name="WcfEssentials.MyFirstService">
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8080/helloworld/"   />
      </baseAddresses>
    </host>
    <endpoint address="ws"  binding="wsHttpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="basic"  binding="basicHttpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="net.tcp://localhost:8081/helloworld/"  binding="netTcpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>
</services>

Uruchomienie aplikacji spowoduje pojawienie się okna, w którym możemy testować nasze endpointy.


Jeżeli chcemy wykorzystać nasz serwis w kodzie, dodajemy osobny projekt, np. aplikację konsolową. Następnie musimy dodać referencję do serwisów WCF. Klikamy prawym przyciskiem myszy i wybieramy Add Service Reference....


Strona kliencka pobierze metadane i utworzy obiekty proxy, którymi będziemy mogli się łączyć do stworzonych wcześniej serwisów. W pliku app.config automatycznie wygenerują się bindingi dla strony klienckiej, gdzie każdy endpoint otrzyma swoją nazwę. Wywołanie serwisu w kodzie C# jest bardzo proste.

using (var client = new MyFirstServiceClient("NetTcpBinding_IMyFirstService"))
{
    var name = new Name() { First = "Tom", Last = "Smith" };
    Console.WriteLine(client.SayHello(name));
    Console.Read();
}

Jako parametr konstruktora podajemy wygenerowaną przez VS nazwę serwisu.

niedziela, 6 października 2013

[SQL] T-SQL: Common Table Expression

CTE występuje tylko w Transact - SQL-u i można go nazwać tymczasowym wynikiem otrzymanym w wyniku jednego zapytania na potrzebny innej operacji. Rozwiązanie takie poprawia czytelność dużych zapytań i może być wykorzystane w różnych sytuacjach:
  • zapytania rekurencyjne
  • odwoływanie się do jakiegoś wyniku wielokrotnie (dzięki nazwanemu CTE odwołujemy się do nazwy)
  • grupowanie wynikające z pewnych operacji agregujących
Poniżej przykład użycia Common Table Expression.

USE Cars;
GO
WITH Rated_Cars_CTE (Make, Score, Price)
AS
( SELECT Marka, ISNULL([Ocena subiektywna],0) + 
ISNULL([Ocena subiektywna],0), [Cena 2008]
FROM dbo.Cars
WHERE [Cena 2008] IS NOT NULL)

SELECT t.RANGE AS [Score Range], count(*) AS [Number of Occurences]
FROM (
  SELECT CASE  
    WHEN score between 0 and 1 THEN ' 0- 1'
    WHEN score between 1 and 2 THEN ' 1- 2'
    WHEN score between 2 and 3 THEN ' 2- 3'
    WHEN score between 3 and 4 THEN ' 3- 4'
    WHEN score between 4 and 5 THEN ' 4- 5'
    WHEN score between 5 and 6 THEN ' 5- 6'
    WHEN score between 6 and 7 THEN ' 6- 7'
    WHEN score between 7 and 8 THEN ' 7- 8'
    WHEN score between 8 and 9 THEN ' 8- 9'
    WHEN score between 9 and 10 THEN ' 9- 10'
    ELSE '10+' END AS RANGE
  FROM Rated_Cars_CTE) t
GROUP BY t.RANGE

sobota, 5 października 2013

[SQL Server] Import / eksport danych z Excela

Gdy chcemy w szybki sposób przenieść małą bazę danych, możemy ją wyeksportować do Excela. Rozpoczynamy od założenia pustego arkusza kalkulacyjnego i zapisania go na dysku. Następnie otwieramy Sql Server Management Studio i logujemy się do uruchomionej instancji MSSQL.

Na liście baz wybieramy tą, którą chcemy wyeksportować i klikamy na nią prawym przyciskiem myszy.





Przechodzimy przez uwierzytelnianie i wybieramy typ eksportu.



Dochodzimy do okna, w którym możemy wybrać te tabele, które chcemy eksportować.


Po wybraniu przechodzimy do ekranu z podsumowaniem, gdzie po wybraniu Finish nastąpi eksport danych.




Dane zostaną wyeksportowane do arkusza o nazwie Words. Import możemy wykonać w symetryczny sposób. Tym razem jako źródło danych wybieramy arkusz Excala podając ścieżkę.