Windows Communication
Foundation
Bartłomiej Zass
ISV Developer Evangelist
Microsoft
Od obiektów do usług
Polimorfizm
Hermetyzacja
Dziedziczenie
Komunikat
Schemat + kontrakt + polisa
Szeroka współpraca
Położenie dowolne
Ścisły związek
Metadane w czasie działania
Obiektowe UsługiKomponentowe
1980 20001990
Zadania serwera aplikacyjnego
• „Pojemnik” na obiekty realizujące daną funkcjonalnośd
– Jak pisad logikę obiektów?
• Infrastruktura: transakcje, sesje, stan…
• Mechanizmy komunikacji
• Skalowalnośd: platforma + „opcje dla programisty”
• Administrator:
– Nadzór nad działaniem „platformowym”
• Zużycie pamięci, procesora, wątki itp.
– Monitorowanie działania „biznesowego”
• O ile będzie rejestrowane w zrozumiały sposób…
Serwer aplikacyjny to urządzenie które dostarcza aplikację do
urządzeo klienckich (za Wikipedia).
Komputer dedykowany do wykonania określonych
programów/zadao.
+ architektura
MTS
Komponenty
Runtime aplikacji
Deklaratywne transakcje
i bezpieczeostwo
Autoryzacja oparta o
role
COM+
Luźno powiązane zdarzenia
Komponentyu kolejkowane
„przepustnica” – nie więcej niż
x komponentów
Enterprise Services
Model programowania w kodzie
zarządzalnym
Oparty o atrybuty, deklaratywny,
konfiguracja w XML
Windows Communication
Foundation
• Komunikaty oparte o XML
• Dowolny transport
• Zorientowane na usługi
• Bezpieczeostwo: Federacja,
CardSpace (dowody tożsamości)
• Hosting - gdziekolwiek
Ewolucja usług aplikacyjnych
-2002 2002-2006 2006-
Problem – komunikacja…
4 podstawowe doktryny SOA
Wyraźne granice
Usługi są
autonomiczne
Usługi dzielą kontrakt
nie klasę
Kompatybilność
określana przez policy
Wprowadzenie do WCF
WCF: Adres, Binding, Kontrakt
Klient Usługa
KomunikatABC A B C
A B C
Adres Binding Kontrakt
(Gdzie) (Jak) (Co)
Endpoint
Endpoint
Endpoint
Encoder
Transport
BasicHttp, WSHttp,
WSDualHttp,
WSFederation
…Context…
NetTcp,
NetNamedPipe,
NetPeerTcp
NetMsmq,
MsmqIntegration
WCF – standardowe bindingi
Binding Interop Bezp. Sesja Trans. Duplex
BasicHttpBinding BP 1.1 N, T N N n/a
WSHttpBinding WS M, T, X N, T, RS N, Tak n/a
WSDualHttpBinding WS M RS N, Tak Tak
WSFederationBinding Federacja M N, RS N, Tak Nie
NetTcpBinding .NET T, M T ,RS N, Tak Tak
NetNamedPipeBinding .NET T T, N N, Tak Tak
NetPeerTcpBinding Peer T N N Tak
NetMsmqBinding .NET T, M, X N N, Tak Nie
MsmqIntegrationBinding MSMQ T N N, Tak n/a
N = Brak | T = Transport | M = Wiadomośd | B = Oba | RS = Pewna sesja
WCF – podstawy (wyjaśnienie)
• Separacja kontraktu i implementacji
• Wzorce komunikacyjne
– Komunikacja jednokierunkowa (IsOneWay)
– Zwracanie wartości
– Sesja,
– Kontrakt „zwrotny” (po stronie klienta)
– Kontekst
• Separacja szczegółów komunikacyjnych
• Hosting: Jakkolwiek
WCF na jednym slajdzie
Definicja „koocówki”
Adres + Binding +
Kontrakt
Definicja kontraktu
Implementacja usługi
[ServiceContract]
public interface IMyInterface {
[OperationContract]
void MyMethod();
[ServiceBehavior(
InstanceContextMode=Single]
public class MyService: IMyInterface {
[OperationBehavior(Impersonation =
ImpersonationOption.Required)]
public void MyMethod() { … }
<service name="MyService">
<endpoint
address=“net.tcp://localhost:1234/MySvc"
binding="netTcpBinding"
contract="IMyInterface" />o
Kontrakt
• Definiują funkcjonalnośd usługi.
• Atrybuty kontraktu:
– ServiceContract
• OperationContract
– DataContract
– FaultContract
– MessageContract
• MessageBody
• MessageHeader
• Zachowania kontraktu
– Sesja, transakcje, sposób inicjacji…
Kontrakt
• Możliwośd implementacji wielu kontraktów
• Publiczny konstruktor
• [ServiceContract(Namespace = „”)+
– Domyślnie – tempuri.org
– Intranet – np. nazwa aplikacji
– Intenet – URL
• Nazwa metody usługi – domyślnie z klasy
– [OperationContract(Name = „…”)+
Host
• IIS + WAS (Vista+, Windows Server 2008+)
– Zarządzanie, skalowalnośd, itp.
• IIS 5/6 – tylko HTTP
– Web.config – eksponowane usługi
• Self-hosting
– InProc – szczególny przypadek (klient i serwer w
tym samym procesie)
– Między procesami
– Między maszynami
ServiceHost
public static void Main( )
{
Uri baseAddress1 = new Uri("net. tcp: //localhost: 8001/");
ServiceHost host1 = new ServiceHost(typeof(MyService),
baseAddress1) ;
ServiceHost host = new ServiceHost(typeof(MyService));
host.Open( );
//Możliwe blokujące wywołania
Application.Run(new MyForm( ));
host.Close( );
}
// WcfSvcHost
WAS
• Nie ograniczony do HTTP
– Dowolny transport, port, kolejka
• Zalety vs self-hosting
– Application pooling, recycling, zarządzanie idle-
time, izolacja
– Zalecane, kiedy dostępne Windows Server 2008
• Interakcja z procesem hosta
<%@ ServiceHost
Language = "C#"
Debug = "true"
CodeBehind = "~/App_Code/MyService. cs"
Service = "MyService"
Factory = "MyServiceFactory" %>
Service Factory
class MyServiceFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type
serviceType, Uri[] baseAddresses)
{
ServiceHost host = new ServiceHost(serviceType,
baseAddresses);
// Dodatkowe kroki – np. logowanie
return host;
}
}
Bindingi
• Protokół (HTTP, TCP, IPC, MSMQ)
• Enkodowanie (plain text, MTOM)
• Bezpieczeostwo (transport, komunikat)
• …
Inne popularne bindingi
• NetPeerTcpBinding
• WSFederationHttpBinding
• WS2007FederationHttpBinding
• MsmqIntegrationBinding
• WebHttpBinding
• WS2007HttpBinding
Wybór bindingu
Endpoint
• „Koocówka”, z którą możemy się
komunikowad
– Przynajmniej jeden dla każdej usługi
• Dokładnie jeden kontrakt
Endpointy - konfiguracja
<system.serviceModel = "">
<services>
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract„
bindingConfiguration = "TransactionalTCP
/>
</service>
</services>
<!–-
<bindings>
<netTcpBinding>
<binding name = "TransactionalTCP"
transactionFlow = "true" />
</netTcpBinding>
</bindings>
-->
</system.serviceModel>
Metadane
• Domyślnie – nie publikowane (nie przeszkadza w działaniu)
• Dedykowany endpoint (MEX)
– Eksponowany w dowolny sposób (HTTP, TCP, IPC)
<endpoint
address = "http: //localhost: 8000/MEX"
binding = "mexHttpBinding"
contract = "IMetadataExchange"
/>
• HTTP-GET (nie ma gwarancji interop)
<behaviors>
<serviceBehaviors>
<behavior name = "MEXGET">
<serviceMetadata httpGetEnabled = "true"/>
</behavior>
</serviceBehaviors>
</behaviors>
Po stronie klienta
• Add service reference – automatycznie
• SvcUtil
SvcUtil http://localhost:8000/MEX /out:Proxy.cs
• WcfTestClient. exe http: //localhost:9000/
• Ręcznie - kod
public abstract class ClientBase<T> : ICommunicationObject, IDisposable
{
protected ClientBase(string endpointName);
protected ClientBase(Binding binding, EndpointAddress remoteAddress);
public void Open();
public void Close();
protected T Channel
{ get; }
//...
}
[ServiceContract(Namespace = "MyNamespace")]
interface IMyContract
{
[OperationContract]
void MyMethod();
}
partial class MyContractClient : ClientBase<IMyContract>, IMyContract
{
public MyContractClient()
{ }
public MyContractClient(string endpointName)
: base(endpointName)
{ }
public MyContractClient(Binding binding, EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{ }
/* Dodatkowe konstruktory */
public void MyMethod()
{
Channel.MyMethod();
}
}
Wywoływanie usługi
MyContractClient proxy = new
MyContractClient("MyEndpoint");
proxy.MyMethod( );
proxy.Close( );
// Można również using…
Bez generacji kodu - ChannelFactory
Binding binding = new NetTcpBinding( );
EndpointAddress address =
new EndpointAddress("net.tcp://localhost:8000");
IMyContract proxy =
ChannelFactory<IMyContract>.CreateChannel(binding, address) ;
using(proxy as IDisposable)
{
proxy.MyMethod( );
}
Transport session
• Niektóre bindingi (np. basic – nie)
• Mapowanie komunikatu na konkretny kontekst wywołujący
operacje usługi
• Reliability
– Transport reliability – pakiety, itp.
– Message reliability – kolejnośd (domyślnie) i informowanie kiedy nie
dostarczono (ponawianie, itp.)
<binding name = "ReliableTCP">
<reliableSession enabled = "true"/>
</binding>
Kolejnośd komunikatów
• Nie powinniśmy wymagad konkretnego bindingu, ale czasem
istotna jest kolejnośd.
[DeliveryRequirements(TargetContract = typeof(IMyContract),
RequireOrderedDelivery = true) ]
class MyService : IMyContract, IMyOtherContract
{ ... }
[ServiceContract]
[DeliveryRequirements(RequireOrderedDelivery = true) ]
interface IMyContract { ... }
Kontrakt
Overloading
// Błąd!
[ServiceContract]
interface ICalculator
{
[OperationContract]
int Add(int arg1, int arg2);
[OperationContract]
double Add(double arg1, double arg2);
}
// Rozwiązanie:
// [OperationContract(Name = „AddInt”)]
// Proxy: AddInt -> również możliwa ręczna zmiana
// nazw i OperationContract
Dziedziczenie
• Możliwe, ale *ServiceContract] nie jest dziedziczony
[ServiceContract]
interface ISimpleCalculator
{
[OperationContract]
int Add(int arg1, int arg2);
}
[ServiceContract]
interface IScientificCalculator : ISimpleCalculator
{
[OperationContract]
int Multiply(int arg1, int arg2);
}
// „Spłaszczane” po stronie klienta
// Możliwe ręczne przywrócenie hierarchii po stronie klienta i usunięcie atrybutów
// [OperationContract]
[OperationContract(Action = "... /ISimpleCalculator/Add",
ReplyAction = "... /ISimpleCalculator/AddResponse")]
int Add(int arg1, int arg2) ;
[OperationContract(Action = "... /IScientificCalculator/Multiply",
ReplyAction = "... /IScientificCalculator/MultiplyResponse")]
int Multiply(int arg1, int arg2) ;
Zgodnośd z kontraktem
// Dynamiczna weryfikacja zgodności na podstawie WSDL
bool contractSupported = false;
Uri mexAddress = new Uri("...?WSDL") ;
ServiceEndpointCollection endpoints =
MetadataResolver. Resolve(typeof(ISimpleCalculator) ,
exAddress, MetadataExchangeClientMode. HttpGet) ;
if(endpoints. Count > 0)
{
contractSupported = true;
}
Kontrakty danych
Wymiana danych
• Infoset – obsługiwane typy parametrów, …
• Serializacja w .NET
– [Serializable], [NonSerialized]
– BinaryFormatter, SoapFormatter – wymaga .NET i
konkretnego typu
– WCF – DataContractSerializer – nie udostępnia
informacji o typie; tylko kontrakt danych
• Wyłącznie serializowalne typy
– Klasa musi byd dostępna także po stronie klienta
Atrybuty kontraktu danych
• Samo [Serializable] – zbyt ścisłe powiązanie
• [DataContract]
– Publikowane w MEX
[DataContract]
struct Contact
{
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
}
// *Dla uproszczenia bez właściwości
// Właściwości mogą być private, koniecznie get i set
Zdarzenia serializacji
• Np. inicjalizacja
• [OnSerializing], [OnSerialized]
[OnDeserializing], [OnDeserialized]
[DataContract]
class MyDataContract
{
[OnSerializing]
void OnSerializing(StreamingContext context) {. . .}
}
Inne
• Współdzielenie kontraktu
– Problem: ten sam typ w 2 serwisach (przestrzenie nazw)
– „Reuse types in reference assemblies”
• Dziedziczenie
– [KnownType], [ServiceKnownType]
// Customer : Contact
Contact contact = new Customer( );
ContactManagerClient proxy = new ContactManagerClient( );
// Błąd w czasie działania
proxy.AddContact(contact) ;
proxy.Close( );
// ------------------------
[DataContract]
[KnownType(typeof(Customer))]
class Contact { ... }
[DataContract]
class Customer : Contact { ... }
Współdzielenie kontraktu
• Zgodnośd z infoset - np. wersjonowanie
• Dostosowanie klasy właściwością Name
[DataContract(Name = "Contact")]
struct Person
{
[DataMember(Name = "FirstName")]
public string Name;
[DataMember(Name = "LastName")]
public string Surname;
}
Współdzielenie a serializacja (1)
• Od najbardziej ogólnej do najbardziej
szczegółowej
• Pola – alfabetycznie
[DataContract]
class Contact
{
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
}
[DataContract]
class Customer : Contact
{
[DataMember]
public int CustomerNumber;
}
// Infoset: Firstname, Lastname, CustomerNumber
Współdzielenie a serializacja (2)
[DataContract(Name = "Customer")]
public class Person
{
[DataMember(Name = "FirstName")]
public string Name;
[DataMember(Name = "LastName")]
public string Surname;
[DataMember]
public int CustomerNumber;
}
// Kolejność: CustomerNumber, Firstname, Lastname
[DataContract(Name = "Customer")]
public class Person
{
[DataMember(Name = "FirstName", Order = 1)]
public string Name;
[DataMember(Name = "LastName", Order = 1)]
public string Surname;
[DataMember(Order = 2)]
public int CustomerNumber;
}
// Kolejność zgodna z infoset: Firstname, Lastname, CustomerNumber
Wersjonowanie kontraktu
• Dodawanie nowych pól
– Nadmiarowośd jest dozwolona i kompatybilna
• Brakujące pola
– Wartości domyślne (null, itp.)
– [OnDeserializing]
– [DataMember(IsRequired = true)] – wyjątek
– Problem z przekazywaniem dalej – utracona
informacja
Wersjonowanie c.d.
[DataContract]
class Contact : IExtensibleDataObject
{
ExtensionDataObject IExtensibleDataObject.ExtensionData
{ get; set; }
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
}
// Nie tracimy nadmiarowej informacji
// Umożliwia to interakcję z serwisem spodziewającym się innego
// kontraktu. Dlatego można wykluczyć taki scenariusz:
[ServiceBehavior(IgnoreExtensionDataObject = true)]
class ContactManager : IContactManager { ... }
// Najlepsza praktyka: zawsze obsługa IExtensibleDataObject
// unikać IgnoreExtensionDataObject
Sposoby
instancjonowania
Kontekst
Zarządzanie kontekstem
• Tryby instancjonowania kontekstów
• Per Call
• Per Session
• Singleton
Per Call
• Bezstanowośd, skalowalnośd
– Konieczna inicjalizacja i zapis stanu
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyService : IMyContract
{ ... }
[ServiceContract]
interface IMyContract
{
[OperationContract]
void MyMethod( );
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall) ]
class MyService : IMyContract, IDisposable
{
int m_Counter = 0;
MyService( )
{
Trace. WriteLine("MyService. MyService( )");
}
public void MyMethod( )
{
m_Counter++;
Trace. WriteLine("Counter = " + m_Counter) ;
}
public void Dispose( )
{
Trace. WriteLine("MyService. Dispose( )");
}
}
///////////////////////// Client Code /////////////////////
MyContractClient proxy = new MyContractClient( );
proxy.MyMethod( );
proxy.MyMethod( );
proxy.Close( );
// Wyjście:
MyService.MyService( )
Counter = 1
MyService.Dispose( )
MyService.MyService( )
Counter = 1
MyService.Dispose( )
Per Call i sesja komunikacyjna
• Jeśli serwis ma tryb single-threaded i
włączone transport session, żądania
przetwarzane jeden po drugim
– Bez sesji – możliwa losowa kolejnośd na wyjściu
• Uwaga: Load Balancer i sesja
– Sticky Sessions
Per Session
• Domyślny tryb
• Instancja utrzymywana w pamięci
– Skalowalnośd
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
class MyService : IMyContract { ... }
• Wymagane transport session
– Zalecane wymuszenie na poziomie kontraktu
public enum SessionMode
{
Allowed,
Required,
NotAllowed
}
[ServiceContract(SessionMode = SessionMode.Allowed)]
interface IMyContract {. . . }
Singleton
• Tworzony wraz ze startem procesu hosta
• Sesja nie wymagana
– Po zakooczeniu, nadal instancja w pamięci
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
class MyService : IMyContract { ... }
• Możliwe przekazanie zainicjalizowanej
instancji klasy do konstruktora hosta
Sesja a kontrakt
• Atrybuty gwarantujące prawidłową kolejnośd
wywołao
[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{
[OperationContract]
void StartSession();
[OperationContract(IsInitiating = false)]
void CannotStart();
[OperationContract(IsTerminating = true)]
void EndSession();
[OperationContract(IsInitiating = false, IsTerminating = true)]
void CannotStartCanEndSession();
}
Zwalnianie kontekstu
• ReleaseInstanceMode.BeforeCall
• ReleaseInstanceMode.AfterCall
• ReleaseInstanceMode.BeforeAndAfterCall
Durable services
• Długotrwałe procesy
– Wywołanie – długi czas oczekiwania - wywołanie
• Trwałe zapisywane stanu (np. baza danych)
– Serializacja – deserializacja
– Provider (domyślny lub własny)
• ID instancji
– Komunikaty aktywacyjne (jeśli brak ID)
– Komunikaty kooczące (usunięcie instancji)
<behaviors>
<serviceBehaviors>
<behavior name = "DurableService">
<persistenceProvider
type = "... type ... , ... assembly ... "
<!-- Provider-specific parameters -->
/>
</behavior>
</serviceBehaviors>
</behaviors>
[Serializable]
[DurableService]
class MyCalculator : ICalculator
{
double Memory
{ get; set; }
[DurableOperation(CanCreateInstance = true)]
public double Add(double number1, double number2)
{
return number1 + number2;
}
public void MemoryStore(double number)
{
Memory = number;
}
[DurableOperation(CompletesInstance = true)]
public void MemoryClear()
{
Memory = 0;
}
//Rest of the implementation
}
Throttling
• Tymczasowe piki w obciążeniu
– Kolejkowanie, spłaszczenie piku
• Nie działa w przypadku stałego zwiększenia obciążenia
• Możliwe do konfiguracji: maxConcurrentCalls,
maxConcurrentSessions, maxConcurrentInstances
Operacje
One-way
• [OperationContract(IsOneWay = true)]
– Przed metodą usługi
– Domyślnie – false
– Metoda musi zwracad void (inaczej błąd)
• Wyjątki usługi nie dotrą do klienta
• Błędy komunikacyjne nadal się pojawiają
• Jeśli reliablesession (transport session) – wtedy jest
przerywana
• Najlepsza praktyka: tylko PerCall i Singleton
Operacje duplex
• Niektóre bindingi
– WSDualHttpBinding, NetTcpBinding, NetNamedPipeBinding
– Nie jest standardem
• Odpowiedź wywoływana przez serwis na klasie klienta
– WSDualHttpBinfing - domyślnie port 80 (możliwa zmiana)
• Wywołanie natychmiastowe lub referencja na poźniej
IMyContractCallback callback =
OperationContext.Current.GetCallbackChannel<IMyContractCallback>( );
interface ISomeCallbackContract
{
[OperationContract]
void OnCallback();
}
[ServiceContract(CallbackContract = typeof(ISomeCallbackContract))]
interface IMyContract
{
[OperationContract]
void DoSomething();
}
Po stronie klienta
class MyCallback : IMyContractCallback
{
public void OnCallback( )
{ ... }
}
IMyContractCallback callback = new MyCallback();
InstanceContext context = new InstanceContext(callback);
MyContractClient proxy = new MyContractClient(context) ;
proxy.DoSomething();
Callback
• Wywoływanie callbacku w operacji usługi
– Domyślnie 1 wątek ma dostęp do metod
– W trakcie wywoływanej operacji lock na instancję
kontekstu
– Po wywołaniu callback – odpowiedź do serwera na
ten sam (zablokowany) kanał
• ConcurrencyMode
– Single
– Multiple – ok, kłopoty z synchronizacją
– Reentrant – ok
• Lub: metody OneWay (brak odpowiedzi)
Streaming
• Klasyczne wywołania – buforowane po stronie
serwera
– Blokowanie do czasu zakooczenia przesyłania
komunikatu
• Streaming (wyłącznie klasa Stream)
– Niektóre bindingi (TCP, IPC, Basic HTTP)
– Niemożliwy do wykorzystania z message-level
security
<bindings>
<basicHttpBinding>
<binding name = "StreamedHTTP"
transferMode = "Streamed„
maxReceivedMessageSize = "120000"/>
</basicHttpBinding>
</bindings>
Streaming c.d.
[ ServiceContract]
interface IMyContract
{
[OperationContract]
Stream StreamReply1( );
[OperationContract]
void StreamReply2(out Stream stream) ;
[OperationContract]
void StreamRequest(Stream stream) ;
[OperationContract(IsOneWay = true)]
void OneWayStream(Stream stream);
}
Błędy
Podstawy
• Błędy komunikacyjne i kanałów
• Błędy po stronie serwisu
– Izolacja błędów od klienta!
• Jeśli jest sesja – automatycznie stan kanału
CommunicationState.Faulted
// w klasycznym .NET ok – tu CommunicationObjectFaultedException
// uwaga na using!- po dispose nowa instancja proxy
IMyContract obj = new MyClass( );
try
{
obj.MyMethod( );
}
catch
{}
obj.MyMethod( );
Propagacja błędów
• Ustandaryzowane – SOAP fault
• FaultException<T>
class Calculator : ICalculator
{
public double Divide(double number1, double number2)
{
if (number2 == 0)
{
DivideByZeroException exception = new DivideByZeroException();
throw new FaultException<DivideByZeroException>(exception);
}
return number1 / number2;
}
}
Kontrakty błędów
• Każdy błąd dociera do klienta jako FaultException
– Także FaultException<T> (dziedziczy po FaultException)
– Wszystko co jest wymieniane musi byd w kontrakcie
• [FaultContract]
– Musi byd dokładnie klasa błędu
– Nie może byd to nawet klasa dziedzicząca!
– Może byd kilka – dla kilku typów
– Nie można aplikowad dla operacji OneWay (błąd)
– Nie zamyka kanału komunikacji (każdy dziedziczący po FaultException)
• Kontrakt publikowany w metadanych
– Klasy błędów importowane podczas generacji proxy
• IErrorHandler
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
double Divide(double number1, double number2) { ... }
// jeśli typeof(Exception) -może być tylko throw Exception!
Debugowanie - ExceptionDetail
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
class MyService : IMyContract
{
public void MethodWithError()
{
throw new InvalidOperationException("Some error");
}
}
// ---------------------------------------
MyContractClient proxy = new MyContractClient( );
try
{
proxy.MethodWithError( );
}
catch(FaultException<ExceptionDetail> exception)
{
Debug.Assert(exception.Detail.Type ==
typeof(InvalidOperationException).ToString( ));
Debug.Assert(exception.Message == "Some error") ;
}
// Może byd ustawiane ręcznie / w konfiguracji hosta (debugowanie istniejącego serwisu!)
// Uwaga przy deployment!
Transakcje
Transakcje
• Wymagane zasoby transakcyjne
– WCF Resource Managers
– Np. baza danych czy kolejka MSMQ, Volatile Resource Managers
– http://msdn.microsoft.com/en-us/magazine/cc163688.aspx#S8
• Transakcje rozproszone
– Two-phase commit i transaction manager
– 1 faza: voting, 2 faza: faktyczny commit
• Protokoły (wybór automatyczny)
– Lightweight – app domain
– OleTX – intranet
– WS-Atomic Trnsaction (WSAT) - internet
Propagacja transakcji
• Obsługują wybrane bindingi
– TCP, IPC, WS http
• Domyślnie – wyłączone
– Konieczne włączenie po stronie klienta i serwera
• Sesja – nie wymagana, ale zalecana
<bindings>
<netTcpBinding>
<binding name = "TransactionalTCP"
transactionFlow = "true"
/>
</netTcpBinding>
</bindings>
Propagacja transakcji c.d.
• Nie wszystkie operacje muszą obsługiwad
transakcje
• [TransactionFlow]
– Allowed
– NotAllowed
– Mandatory
• Operacje OneWay – błąd
[ServiceContract]
interface IMyContract
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void MyMethod();
}
Transaction Manager
• Nadzoruje transakcję
• Lightweight Transaction Manager (LTM)
– AppDomain, SQL 2005/2008
• Kernel Transaction Manager (KTM)
– Vista / Windows Server 2008
– KRM: Transactional file system (TxF), Transaction registry (TxR)
– Tylko jeden serwis
• Distributed Transaction Coordinator (DTC)
– Wykorzystywany w transakcjach rozproszonych
– OleTX / WSAT
• Każda transakcja najpierw zarządzana przez LTM
– Jeśli więcej usług / zasobów – promocja do DTC
– Jeśli zasób KRM – promocja do KTM
Transakcja z DTC
Ambient transaction
• Transakcje wewnątrz thread local storage (TLS)
– Transaction t = Transaction.Current;
• Automatyczne przenoszenie transakcji na stronę
usługi
• Transaction Scope
class MyService : IMyContract
{
[OperationBehavior(TransactionScopeRequired = true)]
public void MyMethod()
{
Transaction transaction = Transaction.Current;
Debug.Assert(transaction != null);
}
}
Przykładowy przepływ
Głosowanie (commit)
• Deklaratywnie
– Zalecane
– domyślnie TransactionAutoComplete = true
– Jeśli mamy strukturę try-catch, wyrzucid wyjątek
dalej
[OperationBehavior(TransactionScopeRequired = true,
TransactionAutoComplete = true)]
public void MyMethod( )
{ ... }
[OperationBehavior(TransactionScopeRequired = true)]
public void MyMethod( )
{ ... }
Głosowanie (kod)
• Zatwierdzenie powinno byd ostatnim wywołaniem w
metodzie
[OperationBehavior(TransactionScopeRequired = true,
TransactionAutoComplete = false) ]
public void MyMethod( )
{
try
{
/* Na koniec: */
OperationContext.Current.SetTransactionComplete( );
}
catch
{
/* np. logowanie, następnie: */
throw;
}
}
Izolacja transakcji
• Domyślnie – Unspecified
– Wykorzystywany poziom transakcji klienta
• Jeśli inny – klient musi mied zadeklarowany
poziom transakcji taki sam (inaczej błąd)
class MyService : IMyContract
{ ... }
[ServiceBehavior(TransactionIsolationLevel =
IsolationLevel.Unspecified)]
class MyService : IMyContract
{ ... }
Klient
using (TransactionScope scope = new TransactionScope())
{
MyContractClient proxy1 = new MyContractClient( );
proxy1.MyMethod( );
proxy1.Close( );
MyOtherContractClient proxy2 = new MyOtherContractClient( );
proxy2.MyOtherMethod( );
proxy2.Close( );
scope.Complete( );
}
Instancjonowanie PerCall
MyContractClient proxy = new MyContractClient( );
using(TransactionScope scope = new TransactionScope( ))
{
proxy.MyMethod( ... ) ;
proxy.MyMethod( ... ) ;
scope.Complete( );
}
proxy.Close( );
Instancjonowanie PerSession
• Domyślnie – podobnie jak PerCall
– Po scope.Complete – instancja usuwana
– ReleaseServiceInstanceOnTransactionComplete
– ConcurrencyMode musi byd Single
[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = true)]
class MyService : IMyContract
{
[OperationBehavior(TransactionScopeRequired = true)]
public void MyMethod( )
{ ... }
[OperationBehavior( ... )]
public void MyOtherMethod( )
{ ... }
}
„Prawdziwe” PerSession
• ReleaseServiceInstanceOnTransactionComplete = false
• Zapisywanie stanu
– Resource Manager – jako klucz identyfikator sesji
OperationContext.Current.SessionId
– Volatile Resource Managers – pola obiektu instancji
PerSession – c.d.
• Możliwe zrównanie czasu trwania sesji z czasem
trwania transakcji
– ReleaseServiceInstanceOnTransactionComplete = false
– TransactionAutoComplete = false
– Uwaga na timeout
• Nie zalecane
[ ServiceBehavior(TransactionAutoCompleteOnSessionClose = true) ]
class MyService : IMyContract {. . . }
Durable Services
• Stan zapisywany w bazie
– Transakcja może niechcący przed tym
powstrzymad
• Domyślnie – zapisywanie stanu nie jest
częścią transakcji
• [SaveStateInOperationTransaction]
– Domyślnie – false
– True – aby stan był zarządzany transakcją
Singleton
• Domyślnie – PerCall
– ReleaseServiceInstanceOnTransactionComplete
• Stan w Volatile Resource Managers
Wielowątkowośd
Tryby
• [ServiceBehavior(ConcurrencyMode=…)+
– Single
– Reentrant
– Multiple
• Single
– Domyślny
– Tylko jeden wątek na raz
• Multiple
– Konieczne synchronizowanie dostępu
• lock { }
• [MethodImpl(MethodImplOptions.Synchronized)]
• W takim wypadku – niewielki zysk
– Limitowane przez throttle – domyślnie 16 połączeo
• Reentrant
Wątek UI
• WPF, Silverlight – Dispatcher
partial class MyForm : Form
{
Label m_CounterLabel;
public SynchronizationContext MySynchronizationContext
{ get; set; }
public MyForm()
{
InitializeComponent();
MySynchronizationContext = SynchronizationContext.Current;
}
void InitializeComponent()
{
. . .
m_CounterLabel = new Label( );
. . .
}
public int Counter
{
get
{
return Convert.ToInt32(m_CounterLabel.Text);
}
set
{
m_CounterLabel.Text = value.ToString();
}
}
}
Synchronization Context – c.d.
[ServiceContract]
interface IFormManager
{
[OperationContract]
void IncrementLabel();
}
class MyService : IFormManager
{
public void IncrementLabel()
{
MyForm form = Application.OpenForms[0] as MyForm;
Debug. Assert(form ! = null) ;
SendOrPostCallback callback = delegate
{
form.Counter++;
};
form.MySynchronizationContext.Send(callback, null);
}
}
static class Program
{
static void Main()
{
ServiceHost host = new ServiceHost(typeof(MyService));
host.Open();
Application.Run(new MyForm());
host.Close();
}
}
Wątek UI
[ServiceBehavior(UseSynchronizationContext = true)]
class MyService : IMyContract {. . . }
• Jeśli wątek uruchamiający usługę ma Synchronization
Context, automatycznie kontekst przechodzi do usługi
• Form jako usługa (i tak ścisłe powiązanie)
– Form jako callback – ok
• Inne rozszerzenia
– Np. Ustawianie priorytetów dla wywołao
http://msdn.microsoft.com/en-us/magazine/cc163321.aspx
Operacje asynchroniczne
• Specjalne pole checkbox podczas dodawania
referencji
• Klasycznie jak w .NET
– Begin<operacja>, End<operacja>
– End – blokuje
– Callback
– Polling – IsCompleted
– Wiele wywołao – WaitAll() i
IAsyncResult.WaitHandle
Serwisy kolejkowane
Serwisy kolejkowane
• Dostępnośd usługi
• Load leveling – skolejkowanie „piku”
• Kolejkowanie niezależnych operacji
biznesowych
• Kompensacja (druga kolejka z wynikami)
• Kolejkowanie w WCF
– NetMsmqBinding
– Opakowanie komunikatu SOAP w komunikat
MSMQ
Architektura
• Klient wywołuje proxy
• Proxy zapisuje komunikat do kolejki
• Usługa netMsmqBinding instaluje Channel Listener
• Channel listener powiadamia o komunikacie –
komunikat zdejmowany i przetwarzany
Kolejkowanie w WCF
• Tylko operacje OneWay
– Nie mogą zwracad wartości
– Nie mogą zwracad błędów
• Kolejki MSMQ
– Public – pomiędzy serwerami w domenie
– Private – lokalne, nie wymagają DC
– Jeden endpoint = jedna kolejka
• Hosting WAS – nazwa kolejki = nazwa pliku svc
– address = "net. msmq: //localhost/private/WASService. svc"
<endpoint
address = "net.msmq: //localhost/private/MyServiceQueue"
binding = "netMsmqBinding"
... />
Transakcje
• MSMQ uczestniczy w transakcjach (opcja)
– Jeśli włączone – komunikaty zapisywane na dysk
– Transakcje playback – nasz kod
• Publiczna kolejka i reliable messeging
– Kolejka proxy
– Automatyczne ponawianie wysłania komunikatu
Sesja i MSMQ
• SessionMode.Allowed lub
SessionMode.NotAllowed – brak sesji
– Każdy komunikat pojedynczo
• SessionMode.Required
– Sessiongram – pakowanie komunikatów w
„paczkę”
– Zachowana kolejnośd doręczenia
Instancjonowanie PerCall
//Klient
using(TransactionScope scope = new TransactionScope( ))
{
MyContractClient proxy = new MyContractClient( );
proxy.MyMethod( ); //Komunikat wysłany
proxy.MyMethod( ); //Komunikat wysłany
proxy.Close( );
scope.Complete( );
} // Zatwierdzane
• Brak przejścia transakcji na stronę usługi
• Każde wywołanie osobno, osobne instancje
Instancjonowanie PerSession
using(TransactionScope scope = new TransactionScope( ))
{
MyContractClient proxy = new MyContractClient( );
proxy.MyMethod();
proxy.MyMethod();
proxy.Close( ); //Skomponowano komunikat, zapis.
scope.Complete( ); //Musi być za proxy.Close!!
} //Pojedynczy komunikat zapisany
Instancjonowanie PerSession
• Nie mogą mied trwającej sesji
– Podobnie do PerCall
• Ale - wszystkie komunikaty do tej samej
instancji
Throttling
• Po włączeniu usługi – wszystkie komunikaty
na raz
– Duża liczba instancji kontekstu – obciążenie
• Umożliwia stopniowe przetwarzanie
Kiedy pojawią się problemy
• Błędy
– Komunikacja
– Bezpieczeostwo
– Quota
– …
• Dead-letter queue (DLQ)
– Problem z doręczeniem
– Nieudany commit transakcji playback
– Przetwarzanie kolejki jak zwykły serwis (kontrakt musi byd
ten sam / dziedziczący)
<! -- Client side -->
<system. serviceModel>
<client>
<endpoint
address = "net. msmq: //localhost/private/MyServiceQueue"
binding = "netMsmqBinding"
bindingConfiguration = "MyCustomDLQ"
contract = "IMyContract"
/>
</client>
<bindings>
<netMsmqBinding>
<binding name = "MyCustomDLQ"
deadLetterQueue = "Custom"
customDeadLetterQueue = "net.msmq: //localhost/private/MyCustomDLQ">
</binding>
</netMsmqBinding>
</bindings>
</system. serviceModel>
<! -- DLQ service side -->
<system. serviceModel>
<services>
<service name = "MyDLQService">
<endpoint
address = "net.msmq://localhost/private/MyCustomDLQ"
binding = "netMsmqBinding"
contract = "IMyContract"
/>
</service>
</services>
</system. serviceModel>
Komunikaty Poison
• Komunikaty Poison
– Nieustanne próby doręczenia
– Zawsze błąd
– Również usługa z polimorficznym kontraktem
• Możliwe akcje
– Drop – odrzucenie i potwierdzenie, że dostarczono
– Reject – odrzucenie i NACK
– Move – przeniesienie do odpowiedniej kolejki
Popularne wzorce
• Response service
– Komunikaty mogą byd jednokierunkowe
– Można zapisywad odpowiedzi w innej kolejce
• HTTP Bridge
– Kolejki – raczej intranet
– Nie interoperacyjne
– Mostek WS-HTTP
Bezpieczeostwo
(podstawy)
Tryby
• Transport
– Negocjacja (protokół), zależy od protokołu
– Weryfikacja integralności treści komunikatu
– Akcelerowane przez karty sieciowe
• Message
– Treśd komunikatu szyfrowana
• Mieszane
– Mix - transport do integralności i szyfrowanie
hasła
– Oba – uwaga na wydajnośd
Bindingi i tryby bezpieczeostwa
Intranet
• Bindingi
– NetTcpBinding
– NetNamedPipeBinding
– NetMsmqBinding
• Transport security
– None
– Signed – tożsamośd nadawcy i integralnośd komunikatu
– Encrypted and signed – dodatkowo szyfrowanie
• Najczęściej – uwierzytelnienie Windows (domain / username / password)
• Autoryzacja – grupy Windows
<bindings>
<netTcpBinding>
<binding name = "TCPWindowsSecurity">
<security mode = "Transport">
<transport
clientCredentialType = "Windows"
protectionLevel = "EncryptAndSign"
/>
</security>
</binding>
</netTcpBinding>
</bindings>
Intranet – c.d.
• ServiceSercurityContext.Current
– IsAnonymous
– PrimaryIdentity
– WindowsIdentity
• Thread.CurrentPrincipal : IPrincipal
• PrincipalPermissionMode
– UseWindowsGroups
– UseAspNetRoles
– None, Custom
• Deklaratywne wymuszanie
– [PrincipalPermission(SecurityAction.Demand, Role = „Manager”)+
• Impersonacja
– Możliwa, ale niezalecana
Internet
• Bindingi
– WSHttpBinding
– WSDualHttpBinding
• Message security
– Szyfrowanie certyfikatem X509
• Certyfikat
– Plik konfiguracyjny (więcej kłopotu ze zmianami)
– Trusted People – zalecane
• Tryb walidacji certyfikatu (certificateValidation)
– PeerTrust – TrustedPeople
– ChainTrust – zaufane root authority: Verisign / Thwart / …
– PeerOrChainTrust
<behavior name = "Internet">
<serviceCredentials>
<serviceCertificate
findValue = "MyServiceCert"
storeLocation = "LocalMachine"
storeName = "My"
x509FindType = "FindBySubjectName"
/>
</serviceCredentials>
</behavior>
Uwierzytelnienie
// Hasło (klasycznie)
MyContractClient proxy = new MyContractClient( );
proxy.ClientCredentials.UserName.UserName = "MyUsername";
proxy.ClientCredentials.UserName.Password = "MyPassword";
proxy.MyMethod( );
proxy.Close( );
// Windows (raczej nie stosowane w Internet, chociaż możliwe)
<behavior name = "UsernameWindows">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode = "Windows"/>
<serviceCertificate
. . .
/>
</serviceCredentials>
</behavior>
Autoryzacja
• Najprościej – Role Provider
– Model znany z ASP.NET
– Dowolne źródło danych (własna lub generowana
baza)
• Duplex
– Odradzane, w prosty sposób autoryzacja
niedostępna
B2B
• Nie współdzielą kont
– Przedstawianie się certyfikatem X509 (klient)
– Dozwoleni klienci instalowani w Trusted People
• Szyfrowanie certyfikatem serwera
• Najczęściej nie potrzebna autoryzacja
– Niewielka liczba lub jeden klient
– Możliwe wykorzystanie Role Providera –
identyfikator certyfikatu jako username
Podsumowanie
Programowanie Web w
WCF
REST, Get, Post, Delete,RSS (i HTTP)
Terminologia
• RSS – prosty format XML dla zestawu danych
• ATOM – jak RSS, ale bardziej ustrukturalizowany
• Syndykacja (Syndication) – nadzbiór RSS, ATOM, inne
• AJAX – DHTML + JavaScript + asynchroniczne
przetwarzanie
• JSON – Format danych (tablicy) w JavaScript
• REST – …
• POX – plain old XML (bez koperty SOAP)
REST
• Protokół HTTP został zaprojektowany z 8 słowami
• Low REST – API bazujące na GET & POST
– Też tak działa przeglądarka
• High REST sugeruje stosowanie 4 głównych słow
– GET/PUT/DELETE/POST
– CRUD dla Web
• Wywoływanie usługi bez warstwy „komunikatu” czy „koperty”
– …sam HTTP
• Oparte na
– URI do definowania endpoint (zasób)
• Spacje & URITemplates
– Polecenia HTTP definiują operacje
• GET/PUT/DELETE/POST
– Typy zawartości
• XML , JSON, <mikroformaty>
Aplikacje REST - atrybuty
• WebGet: Atrybut do oznaczania operacji które są
wołane przez HTTP GET
• webHttpBinding: Binding dla usług REST
• webServiceHost: Klasa do hostowania usług REST
• webServiceHostFactory: Klasa do hostowania usług
REST które nie wymagają pliku konfiguracyjnego
[OperationContract]
[WebGet(UriTemplate=“/WeatherMap/{country}/{zipcode}”)]
Stream GetWeatherMap(String country, String zipcode);
Developing REST Applications
WCF & URI Spaces: URITemplate
• QueryString syntax still available
• URITemplate: formalism for binding URI structure to
method parameters
[OperationContract]
[WebGet(UriTemplate=“/WeatherMap/{country}/{zipcode}”)]
Stream GetWeatherMap(String country, String zipcode);
http://myserver/WeatherMap/USA/98052
http://myserver/GetWeatherMap?country=USA&zipcode=98052
Developing REST Applications
WCF & Transfer: HTTP verbs support
• WebInvoke: new attribute for making a method accept
any non-GET verb
– PUT, DELETE, POST...
[OperationContract]
[WebInvoke(METHOD=“PUT”)]
WeatherReport UploadWeatherReport(WeatherReport theReport);
Ręczne modelowanie URI
• Pomoc: System.UriTemplate
Uri address = new Uri(“http://localhost:2000”);
UriTemplate template =
new UriTemplate(“{artist}/{album}”);
Uri boundUri =
template.BindByPosition(address,
“Northwind”, “Overdone”);
UriTemplateMatch match = template.Match(address,
boundUri);
String bandName = match.BoundVariables[“artist”];
URI w kontrakcie WCF
• Składnia QueryString nadal dostępna
• Typ UriTemplate mapuje na parametry
– Jako atrybut albo jako oddzielny typ
[OperationContract]
[WebGet(UriTemplate=“/Image/{bandName}/{album}”)]
Stream GetAlbumImage(String bandName, String album);
[OperationContract]
[WebGet(UriTemplate=“/Image?name={bandName})]
Stream GetMainImage(String bandName);
Kontrakt typu zobacz / zrób
[OperationContract]
[WebGet(UriTemplate=“/Image/{bandName}/{album}”)]
Stream GetAlbumImage(String bandName, String album);
[OperationContract]
[WebInvoke(METHOD=“PUT”)] // {PUT, POST, DELETE}
void AddAlbum(AlbumInfo albumInfo);
REST – zwracanie wartości
• Typ zawartości zależy od:
– Zachowanie enableWebScript (JSON) albo ustawione w
klasie hostującej
– Atrybutu Response.Format
– Ręcznie:
• WebOperationContext.Current.OutgoingResponse.Conte
ntType
[OperationContract]
[WebGet(UriTemplate =
"WeatherReport/{country}/{zipcode}/JSON",
ResponseFormat=WebMessageFormat.Json)]
WeatherReport GetWeatherReportWithTemplateJSON(string
country, string zipcode);
JSON – co to jest?
• JavaScript Object Notation
• Format do łączenia JavaScript i obiektów
– Łatwiejsze niż XML
• Użycie
– ASP.NET AJAX
– Skrypty JS
– Dynamiczny kod…
var data = {“temp” : 59, “descr” : “cloudy”};
document.write (“The weather is “ + data.descr);
Konwencje związane z danymi
[OperationContract(Name=“TestOp”)]
[WebInvoke(METHOD=“PUT”)] // {PUT, POST, DELETE}
String[] AddAlbum(AlbumInfo albumInfo);
PUT /albumservice/AddAlbum HTTP 1.1
HOST: contoso.com
<albumInfo>
<Name>Hysteria</Name>
<RelDate>8/3/1987</RelDate>
...
</albumInfo>
•XML Namespace
pochodzi z kontraktu
•Nazwy parametrów z
sygnatury operacji
Zwrócenie rysunku w WCF
Stream GetAlbumImage(String bandName, String album){
Stream stream;
// obraz „skądś”
// ustawiamy ContentType i zwracamy strumień
WebOperationContext.Current.OutgoingResponse.ContentType =
“image/jpeg”;
return stream;
}
WCF i syndykacja
Syndykacja: Gromadzenie danych z różnych źródeł w
jednym miejscu (blogi, artykuły itp.)
SyndicationFeed: pojęcie „feedu” niezależne od
formatu
SyndicationFeedFormatter<>
[ServiceKnownType(typeof(Atom10FeedFormatter))]
[ServiceKnownType(typeof(Rss20FeedFormatter))]
[ServiceContract]
interface IPictureSyndication {
[OperationContract]
[WebGet(UriTemplate=“Images/{format}")]
SyndicationFeedFormatter<SyndicationFeed>
GetImage(String format);
}
Kontekst bez protokołu WS=*
• Konwersacja bazująca na kanale komunikacyjnym -
problem
• Konwencja wywołania
– W tle
• Binding:
basicHttpContextBinding
NetTcpContextBinding
BasicHttpContextBinding
• Behavior:
– <persistenceProvider>
• Ten sam co w WF 
• Uwaga! Klient musi też byd świadomy kontekstu
– WCF Service Host nie jest…

Windows Communication Foundation

  • 1.
  • 2.
    Od obiektów dousług Polimorfizm Hermetyzacja Dziedziczenie Komunikat Schemat + kontrakt + polisa Szeroka współpraca Położenie dowolne Ścisły związek Metadane w czasie działania Obiektowe UsługiKomponentowe 1980 20001990
  • 3.
    Zadania serwera aplikacyjnego •„Pojemnik” na obiekty realizujące daną funkcjonalnośd – Jak pisad logikę obiektów? • Infrastruktura: transakcje, sesje, stan… • Mechanizmy komunikacji • Skalowalnośd: platforma + „opcje dla programisty” • Administrator: – Nadzór nad działaniem „platformowym” • Zużycie pamięci, procesora, wątki itp. – Monitorowanie działania „biznesowego” • O ile będzie rejestrowane w zrozumiały sposób… Serwer aplikacyjny to urządzenie które dostarcza aplikację do urządzeo klienckich (za Wikipedia). Komputer dedykowany do wykonania określonych programów/zadao. + architektura
  • 4.
    MTS Komponenty Runtime aplikacji Deklaratywne transakcje ibezpieczeostwo Autoryzacja oparta o role COM+ Luźno powiązane zdarzenia Komponentyu kolejkowane „przepustnica” – nie więcej niż x komponentów Enterprise Services Model programowania w kodzie zarządzalnym Oparty o atrybuty, deklaratywny, konfiguracja w XML Windows Communication Foundation • Komunikaty oparte o XML • Dowolny transport • Zorientowane na usługi • Bezpieczeostwo: Federacja, CardSpace (dowody tożsamości) • Hosting - gdziekolwiek Ewolucja usług aplikacyjnych
  • 5.
  • 6.
    4 podstawowe doktrynySOA Wyraźne granice Usługi są autonomiczne Usługi dzielą kontrakt nie klasę Kompatybilność określana przez policy
  • 7.
  • 8.
    WCF: Adres, Binding,Kontrakt Klient Usługa KomunikatABC A B C A B C Adres Binding Kontrakt (Gdzie) (Jak) (Co) Endpoint Endpoint Endpoint Encoder Transport BasicHttp, WSHttp, WSDualHttp, WSFederation …Context… NetTcp, NetNamedPipe, NetPeerTcp NetMsmq, MsmqIntegration
  • 9.
    WCF – standardowebindingi Binding Interop Bezp. Sesja Trans. Duplex BasicHttpBinding BP 1.1 N, T N N n/a WSHttpBinding WS M, T, X N, T, RS N, Tak n/a WSDualHttpBinding WS M RS N, Tak Tak WSFederationBinding Federacja M N, RS N, Tak Nie NetTcpBinding .NET T, M T ,RS N, Tak Tak NetNamedPipeBinding .NET T T, N N, Tak Tak NetPeerTcpBinding Peer T N N Tak NetMsmqBinding .NET T, M, X N N, Tak Nie MsmqIntegrationBinding MSMQ T N N, Tak n/a N = Brak | T = Transport | M = Wiadomośd | B = Oba | RS = Pewna sesja
  • 10.
    WCF – podstawy(wyjaśnienie) • Separacja kontraktu i implementacji • Wzorce komunikacyjne – Komunikacja jednokierunkowa (IsOneWay) – Zwracanie wartości – Sesja, – Kontrakt „zwrotny” (po stronie klienta) – Kontekst • Separacja szczegółów komunikacyjnych • Hosting: Jakkolwiek
  • 11.
    WCF na jednymslajdzie Definicja „koocówki” Adres + Binding + Kontrakt Definicja kontraktu Implementacja usługi [ServiceContract] public interface IMyInterface { [OperationContract] void MyMethod(); [ServiceBehavior( InstanceContextMode=Single] public class MyService: IMyInterface { [OperationBehavior(Impersonation = ImpersonationOption.Required)] public void MyMethod() { … } <service name="MyService"> <endpoint address=“net.tcp://localhost:1234/MySvc" binding="netTcpBinding" contract="IMyInterface" />o
  • 12.
    Kontrakt • Definiują funkcjonalnośdusługi. • Atrybuty kontraktu: – ServiceContract • OperationContract – DataContract – FaultContract – MessageContract • MessageBody • MessageHeader • Zachowania kontraktu – Sesja, transakcje, sposób inicjacji…
  • 13.
    Kontrakt • Możliwośd implementacjiwielu kontraktów • Publiczny konstruktor • [ServiceContract(Namespace = „”)+ – Domyślnie – tempuri.org – Intranet – np. nazwa aplikacji – Intenet – URL • Nazwa metody usługi – domyślnie z klasy – [OperationContract(Name = „…”)+
  • 14.
    Host • IIS +WAS (Vista+, Windows Server 2008+) – Zarządzanie, skalowalnośd, itp. • IIS 5/6 – tylko HTTP – Web.config – eksponowane usługi • Self-hosting – InProc – szczególny przypadek (klient i serwer w tym samym procesie) – Między procesami – Między maszynami
  • 15.
    ServiceHost public static voidMain( ) { Uri baseAddress1 = new Uri("net. tcp: //localhost: 8001/"); ServiceHost host1 = new ServiceHost(typeof(MyService), baseAddress1) ; ServiceHost host = new ServiceHost(typeof(MyService)); host.Open( ); //Możliwe blokujące wywołania Application.Run(new MyForm( )); host.Close( ); } // WcfSvcHost
  • 16.
    WAS • Nie ograniczonydo HTTP – Dowolny transport, port, kolejka • Zalety vs self-hosting – Application pooling, recycling, zarządzanie idle- time, izolacja – Zalecane, kiedy dostępne Windows Server 2008 • Interakcja z procesem hosta <%@ ServiceHost Language = "C#" Debug = "true" CodeBehind = "~/App_Code/MyService. cs" Service = "MyService" Factory = "MyServiceFactory" %>
  • 17.
    Service Factory class MyServiceFactory: ServiceHostFactory { protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) { ServiceHost host = new ServiceHost(serviceType, baseAddresses); // Dodatkowe kroki – np. logowanie return host; } }
  • 18.
    Bindingi • Protokół (HTTP,TCP, IPC, MSMQ) • Enkodowanie (plain text, MTOM) • Bezpieczeostwo (transport, komunikat) • …
  • 19.
    Inne popularne bindingi •NetPeerTcpBinding • WSFederationHttpBinding • WS2007FederationHttpBinding • MsmqIntegrationBinding • WebHttpBinding • WS2007HttpBinding
  • 20.
  • 21.
    Endpoint • „Koocówka”, zktórą możemy się komunikowad – Przynajmniej jeden dla każdej usługi • Dokładnie jeden kontrakt
  • 22.
    Endpointy - konfiguracja <system.serviceModel= ""> <services> <service name = "MyNamespace.MyService"> <endpoint address = "http://localhost:8000/MyService" binding = "wsHttpBinding" contract = "MyNamespace.IMyContract„ bindingConfiguration = "TransactionalTCP /> </service> </services> <!–- <bindings> <netTcpBinding> <binding name = "TransactionalTCP" transactionFlow = "true" /> </netTcpBinding> </bindings> --> </system.serviceModel>
  • 23.
    Metadane • Domyślnie –nie publikowane (nie przeszkadza w działaniu) • Dedykowany endpoint (MEX) – Eksponowany w dowolny sposób (HTTP, TCP, IPC) <endpoint address = "http: //localhost: 8000/MEX" binding = "mexHttpBinding" contract = "IMetadataExchange" /> • HTTP-GET (nie ma gwarancji interop) <behaviors> <serviceBehaviors> <behavior name = "MEXGET"> <serviceMetadata httpGetEnabled = "true"/> </behavior> </serviceBehaviors> </behaviors>
  • 24.
    Po stronie klienta •Add service reference – automatycznie • SvcUtil SvcUtil http://localhost:8000/MEX /out:Proxy.cs • WcfTestClient. exe http: //localhost:9000/ • Ręcznie - kod public abstract class ClientBase<T> : ICommunicationObject, IDisposable { protected ClientBase(string endpointName); protected ClientBase(Binding binding, EndpointAddress remoteAddress); public void Open(); public void Close(); protected T Channel { get; } //... }
  • 25.
    [ServiceContract(Namespace = "MyNamespace")] interfaceIMyContract { [OperationContract] void MyMethod(); } partial class MyContractClient : ClientBase<IMyContract>, IMyContract { public MyContractClient() { } public MyContractClient(string endpointName) : base(endpointName) { } public MyContractClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { } /* Dodatkowe konstruktory */ public void MyMethod() { Channel.MyMethod(); } }
  • 26.
    Wywoływanie usługi MyContractClient proxy= new MyContractClient("MyEndpoint"); proxy.MyMethod( ); proxy.Close( ); // Można również using…
  • 27.
    Bez generacji kodu- ChannelFactory Binding binding = new NetTcpBinding( ); EndpointAddress address = new EndpointAddress("net.tcp://localhost:8000"); IMyContract proxy = ChannelFactory<IMyContract>.CreateChannel(binding, address) ; using(proxy as IDisposable) { proxy.MyMethod( ); }
  • 28.
    Transport session • Niektórebindingi (np. basic – nie) • Mapowanie komunikatu na konkretny kontekst wywołujący operacje usługi • Reliability – Transport reliability – pakiety, itp. – Message reliability – kolejnośd (domyślnie) i informowanie kiedy nie dostarczono (ponawianie, itp.) <binding name = "ReliableTCP"> <reliableSession enabled = "true"/> </binding>
  • 29.
    Kolejnośd komunikatów • Niepowinniśmy wymagad konkretnego bindingu, ale czasem istotna jest kolejnośd. [DeliveryRequirements(TargetContract = typeof(IMyContract), RequireOrderedDelivery = true) ] class MyService : IMyContract, IMyOtherContract { ... } [ServiceContract] [DeliveryRequirements(RequireOrderedDelivery = true) ] interface IMyContract { ... }
  • 30.
  • 31.
    Overloading // Błąd! [ServiceContract] interface ICalculator { [OperationContract] intAdd(int arg1, int arg2); [OperationContract] double Add(double arg1, double arg2); } // Rozwiązanie: // [OperationContract(Name = „AddInt”)] // Proxy: AddInt -> również możliwa ręczna zmiana // nazw i OperationContract
  • 32.
    Dziedziczenie • Możliwe, ale*ServiceContract] nie jest dziedziczony [ServiceContract] interface ISimpleCalculator { [OperationContract] int Add(int arg1, int arg2); } [ServiceContract] interface IScientificCalculator : ISimpleCalculator { [OperationContract] int Multiply(int arg1, int arg2); } // „Spłaszczane” po stronie klienta // Możliwe ręczne przywrócenie hierarchii po stronie klienta i usunięcie atrybutów // [OperationContract] [OperationContract(Action = "... /ISimpleCalculator/Add", ReplyAction = "... /ISimpleCalculator/AddResponse")] int Add(int arg1, int arg2) ; [OperationContract(Action = "... /IScientificCalculator/Multiply", ReplyAction = "... /IScientificCalculator/MultiplyResponse")] int Multiply(int arg1, int arg2) ;
  • 33.
    Zgodnośd z kontraktem //Dynamiczna weryfikacja zgodności na podstawie WSDL bool contractSupported = false; Uri mexAddress = new Uri("...?WSDL") ; ServiceEndpointCollection endpoints = MetadataResolver. Resolve(typeof(ISimpleCalculator) , exAddress, MetadataExchangeClientMode. HttpGet) ; if(endpoints. Count > 0) { contractSupported = true; }
  • 34.
  • 35.
    Wymiana danych • Infoset– obsługiwane typy parametrów, … • Serializacja w .NET – [Serializable], [NonSerialized] – BinaryFormatter, SoapFormatter – wymaga .NET i konkretnego typu – WCF – DataContractSerializer – nie udostępnia informacji o typie; tylko kontrakt danych • Wyłącznie serializowalne typy – Klasa musi byd dostępna także po stronie klienta
  • 36.
    Atrybuty kontraktu danych •Samo [Serializable] – zbyt ścisłe powiązanie • [DataContract] – Publikowane w MEX [DataContract] struct Contact { [DataMember] public string FirstName; [DataMember] public string LastName; } // *Dla uproszczenia bez właściwości // Właściwości mogą być private, koniecznie get i set
  • 37.
    Zdarzenia serializacji • Np.inicjalizacja • [OnSerializing], [OnSerialized] [OnDeserializing], [OnDeserialized] [DataContract] class MyDataContract { [OnSerializing] void OnSerializing(StreamingContext context) {. . .} }
  • 38.
    Inne • Współdzielenie kontraktu –Problem: ten sam typ w 2 serwisach (przestrzenie nazw) – „Reuse types in reference assemblies” • Dziedziczenie – [KnownType], [ServiceKnownType] // Customer : Contact Contact contact = new Customer( ); ContactManagerClient proxy = new ContactManagerClient( ); // Błąd w czasie działania proxy.AddContact(contact) ; proxy.Close( ); // ------------------------ [DataContract] [KnownType(typeof(Customer))] class Contact { ... } [DataContract] class Customer : Contact { ... }
  • 39.
    Współdzielenie kontraktu • Zgodnośdz infoset - np. wersjonowanie • Dostosowanie klasy właściwością Name [DataContract(Name = "Contact")] struct Person { [DataMember(Name = "FirstName")] public string Name; [DataMember(Name = "LastName")] public string Surname; }
  • 40.
    Współdzielenie a serializacja(1) • Od najbardziej ogólnej do najbardziej szczegółowej • Pola – alfabetycznie [DataContract] class Contact { [DataMember] public string FirstName; [DataMember] public string LastName; } [DataContract] class Customer : Contact { [DataMember] public int CustomerNumber; } // Infoset: Firstname, Lastname, CustomerNumber
  • 41.
    Współdzielenie a serializacja(2) [DataContract(Name = "Customer")] public class Person { [DataMember(Name = "FirstName")] public string Name; [DataMember(Name = "LastName")] public string Surname; [DataMember] public int CustomerNumber; } // Kolejność: CustomerNumber, Firstname, Lastname [DataContract(Name = "Customer")] public class Person { [DataMember(Name = "FirstName", Order = 1)] public string Name; [DataMember(Name = "LastName", Order = 1)] public string Surname; [DataMember(Order = 2)] public int CustomerNumber; } // Kolejność zgodna z infoset: Firstname, Lastname, CustomerNumber
  • 42.
    Wersjonowanie kontraktu • Dodawanienowych pól – Nadmiarowośd jest dozwolona i kompatybilna • Brakujące pola – Wartości domyślne (null, itp.) – [OnDeserializing] – [DataMember(IsRequired = true)] – wyjątek – Problem z przekazywaniem dalej – utracona informacja
  • 43.
    Wersjonowanie c.d. [DataContract] class Contact: IExtensibleDataObject { ExtensionDataObject IExtensibleDataObject.ExtensionData { get; set; } [DataMember] public string FirstName; [DataMember] public string LastName; } // Nie tracimy nadmiarowej informacji // Umożliwia to interakcję z serwisem spodziewającym się innego // kontraktu. Dlatego można wykluczyć taki scenariusz: [ServiceBehavior(IgnoreExtensionDataObject = true)] class ContactManager : IContactManager { ... } // Najlepsza praktyka: zawsze obsługa IExtensibleDataObject // unikać IgnoreExtensionDataObject
  • 44.
  • 45.
  • 46.
    Zarządzanie kontekstem • Trybyinstancjonowania kontekstów • Per Call • Per Session • Singleton
  • 47.
    Per Call • Bezstanowośd,skalowalnośd – Konieczna inicjalizacja i zapis stanu [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract { ... }
  • 48.
    [ServiceContract] interface IMyContract { [OperationContract] void MyMethod(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall) ] class MyService : IMyContract, IDisposable { int m_Counter = 0; MyService( ) { Trace. WriteLine("MyService. MyService( )"); } public void MyMethod( ) { m_Counter++; Trace. WriteLine("Counter = " + m_Counter) ; } public void Dispose( ) { Trace. WriteLine("MyService. Dispose( )"); } } ///////////////////////// Client Code ///////////////////// MyContractClient proxy = new MyContractClient( ); proxy.MyMethod( ); proxy.MyMethod( ); proxy.Close( ); // Wyjście: MyService.MyService( ) Counter = 1 MyService.Dispose( ) MyService.MyService( ) Counter = 1 MyService.Dispose( )
  • 49.
    Per Call isesja komunikacyjna • Jeśli serwis ma tryb single-threaded i włączone transport session, żądania przetwarzane jeden po drugim – Bez sesji – możliwa losowa kolejnośd na wyjściu • Uwaga: Load Balancer i sesja – Sticky Sessions
  • 50.
    Per Session • Domyślnytryb • Instancja utrzymywana w pamięci – Skalowalnośd [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] class MyService : IMyContract { ... } • Wymagane transport session – Zalecane wymuszenie na poziomie kontraktu public enum SessionMode { Allowed, Required, NotAllowed } [ServiceContract(SessionMode = SessionMode.Allowed)] interface IMyContract {. . . }
  • 51.
    Singleton • Tworzony wrazze startem procesu hosta • Sesja nie wymagana – Po zakooczeniu, nadal instancja w pamięci [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] class MyService : IMyContract { ... } • Możliwe przekazanie zainicjalizowanej instancji klasy do konstruktora hosta
  • 52.
    Sesja a kontrakt •Atrybuty gwarantujące prawidłową kolejnośd wywołao [ServiceContract(SessionMode = SessionMode.Required)] interface IMyContract { [OperationContract] void StartSession(); [OperationContract(IsInitiating = false)] void CannotStart(); [OperationContract(IsTerminating = true)] void EndSession(); [OperationContract(IsInitiating = false, IsTerminating = true)] void CannotStartCanEndSession(); }
  • 53.
    Zwalnianie kontekstu • ReleaseInstanceMode.BeforeCall •ReleaseInstanceMode.AfterCall • ReleaseInstanceMode.BeforeAndAfterCall
  • 54.
    Durable services • Długotrwałeprocesy – Wywołanie – długi czas oczekiwania - wywołanie • Trwałe zapisywane stanu (np. baza danych) – Serializacja – deserializacja – Provider (domyślny lub własny) • ID instancji – Komunikaty aktywacyjne (jeśli brak ID) – Komunikaty kooczące (usunięcie instancji) <behaviors> <serviceBehaviors> <behavior name = "DurableService"> <persistenceProvider type = "... type ... , ... assembly ... " <!-- Provider-specific parameters --> /> </behavior> </serviceBehaviors> </behaviors>
  • 55.
    [Serializable] [DurableService] class MyCalculator :ICalculator { double Memory { get; set; } [DurableOperation(CanCreateInstance = true)] public double Add(double number1, double number2) { return number1 + number2; } public void MemoryStore(double number) { Memory = number; } [DurableOperation(CompletesInstance = true)] public void MemoryClear() { Memory = 0; } //Rest of the implementation }
  • 56.
    Throttling • Tymczasowe pikiw obciążeniu – Kolejkowanie, spłaszczenie piku • Nie działa w przypadku stałego zwiększenia obciążenia • Możliwe do konfiguracji: maxConcurrentCalls, maxConcurrentSessions, maxConcurrentInstances
  • 57.
  • 58.
    One-way • [OperationContract(IsOneWay =true)] – Przed metodą usługi – Domyślnie – false – Metoda musi zwracad void (inaczej błąd) • Wyjątki usługi nie dotrą do klienta • Błędy komunikacyjne nadal się pojawiają • Jeśli reliablesession (transport session) – wtedy jest przerywana • Najlepsza praktyka: tylko PerCall i Singleton
  • 59.
    Operacje duplex • Niektórebindingi – WSDualHttpBinding, NetTcpBinding, NetNamedPipeBinding – Nie jest standardem • Odpowiedź wywoływana przez serwis na klasie klienta – WSDualHttpBinfing - domyślnie port 80 (możliwa zmiana) • Wywołanie natychmiastowe lub referencja na poźniej IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>( ); interface ISomeCallbackContract { [OperationContract] void OnCallback(); } [ServiceContract(CallbackContract = typeof(ISomeCallbackContract))] interface IMyContract { [OperationContract] void DoSomething(); }
  • 60.
    Po stronie klienta classMyCallback : IMyContractCallback { public void OnCallback( ) { ... } } IMyContractCallback callback = new MyCallback(); InstanceContext context = new InstanceContext(callback); MyContractClient proxy = new MyContractClient(context) ; proxy.DoSomething();
  • 61.
    Callback • Wywoływanie callbackuw operacji usługi – Domyślnie 1 wątek ma dostęp do metod – W trakcie wywoływanej operacji lock na instancję kontekstu – Po wywołaniu callback – odpowiedź do serwera na ten sam (zablokowany) kanał • ConcurrencyMode – Single – Multiple – ok, kłopoty z synchronizacją – Reentrant – ok • Lub: metody OneWay (brak odpowiedzi)
  • 62.
    Streaming • Klasyczne wywołania– buforowane po stronie serwera – Blokowanie do czasu zakooczenia przesyłania komunikatu • Streaming (wyłącznie klasa Stream) – Niektóre bindingi (TCP, IPC, Basic HTTP) – Niemożliwy do wykorzystania z message-level security <bindings> <basicHttpBinding> <binding name = "StreamedHTTP" transferMode = "Streamed„ maxReceivedMessageSize = "120000"/> </basicHttpBinding> </bindings>
  • 63.
    Streaming c.d. [ ServiceContract] interfaceIMyContract { [OperationContract] Stream StreamReply1( ); [OperationContract] void StreamReply2(out Stream stream) ; [OperationContract] void StreamRequest(Stream stream) ; [OperationContract(IsOneWay = true)] void OneWayStream(Stream stream); }
  • 64.
  • 65.
    Podstawy • Błędy komunikacyjnei kanałów • Błędy po stronie serwisu – Izolacja błędów od klienta! • Jeśli jest sesja – automatycznie stan kanału CommunicationState.Faulted // w klasycznym .NET ok – tu CommunicationObjectFaultedException // uwaga na using!- po dispose nowa instancja proxy IMyContract obj = new MyClass( ); try { obj.MyMethod( ); } catch {} obj.MyMethod( );
  • 66.
    Propagacja błędów • Ustandaryzowane– SOAP fault • FaultException<T> class Calculator : ICalculator { public double Divide(double number1, double number2) { if (number2 == 0) { DivideByZeroException exception = new DivideByZeroException(); throw new FaultException<DivideByZeroException>(exception); } return number1 / number2; } }
  • 67.
    Kontrakty błędów • Każdybłąd dociera do klienta jako FaultException – Także FaultException<T> (dziedziczy po FaultException) – Wszystko co jest wymieniane musi byd w kontrakcie • [FaultContract] – Musi byd dokładnie klasa błędu – Nie może byd to nawet klasa dziedzicząca! – Może byd kilka – dla kilku typów – Nie można aplikowad dla operacji OneWay (błąd) – Nie zamyka kanału komunikacji (każdy dziedziczący po FaultException) • Kontrakt publikowany w metadanych – Klasy błędów importowane podczas generacji proxy • IErrorHandler [OperationContract] [FaultContract(typeof(DivideByZeroException))] double Divide(double number1, double number2) { ... } // jeśli typeof(Exception) -może być tylko throw Exception!
  • 68.
    Debugowanie - ExceptionDetail [ServiceBehavior(IncludeExceptionDetailInFaults= true)] class MyService : IMyContract { public void MethodWithError() { throw new InvalidOperationException("Some error"); } } // --------------------------------------- MyContractClient proxy = new MyContractClient( ); try { proxy.MethodWithError( ); } catch(FaultException<ExceptionDetail> exception) { Debug.Assert(exception.Detail.Type == typeof(InvalidOperationException).ToString( )); Debug.Assert(exception.Message == "Some error") ; } // Może byd ustawiane ręcznie / w konfiguracji hosta (debugowanie istniejącego serwisu!) // Uwaga przy deployment!
  • 69.
  • 70.
    Transakcje • Wymagane zasobytransakcyjne – WCF Resource Managers – Np. baza danych czy kolejka MSMQ, Volatile Resource Managers – http://msdn.microsoft.com/en-us/magazine/cc163688.aspx#S8 • Transakcje rozproszone – Two-phase commit i transaction manager – 1 faza: voting, 2 faza: faktyczny commit • Protokoły (wybór automatyczny) – Lightweight – app domain – OleTX – intranet – WS-Atomic Trnsaction (WSAT) - internet
  • 71.
    Propagacja transakcji • Obsługująwybrane bindingi – TCP, IPC, WS http • Domyślnie – wyłączone – Konieczne włączenie po stronie klienta i serwera • Sesja – nie wymagana, ale zalecana <bindings> <netTcpBinding> <binding name = "TransactionalTCP" transactionFlow = "true" /> </netTcpBinding> </bindings>
  • 72.
    Propagacja transakcji c.d. •Nie wszystkie operacje muszą obsługiwad transakcje • [TransactionFlow] – Allowed – NotAllowed – Mandatory • Operacje OneWay – błąd [ServiceContract] interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); }
  • 73.
    Transaction Manager • Nadzorujetransakcję • Lightweight Transaction Manager (LTM) – AppDomain, SQL 2005/2008 • Kernel Transaction Manager (KTM) – Vista / Windows Server 2008 – KRM: Transactional file system (TxF), Transaction registry (TxR) – Tylko jeden serwis • Distributed Transaction Coordinator (DTC) – Wykorzystywany w transakcjach rozproszonych – OleTX / WSAT • Każda transakcja najpierw zarządzana przez LTM – Jeśli więcej usług / zasobów – promocja do DTC – Jeśli zasób KRM – promocja do KTM
  • 74.
  • 75.
    Ambient transaction • Transakcjewewnątrz thread local storage (TLS) – Transaction t = Transaction.Current; • Automatyczne przenoszenie transakcji na stronę usługi • Transaction Scope class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { Transaction transaction = Transaction.Current; Debug.Assert(transaction != null); } }
  • 76.
  • 77.
    Głosowanie (commit) • Deklaratywnie –Zalecane – domyślnie TransactionAutoComplete = true – Jeśli mamy strukturę try-catch, wyrzucid wyjątek dalej [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void MyMethod( ) { ... } [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod( ) { ... }
  • 78.
    Głosowanie (kod) • Zatwierdzeniepowinno byd ostatnim wywołaniem w metodzie [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false) ] public void MyMethod( ) { try { /* Na koniec: */ OperationContext.Current.SetTransactionComplete( ); } catch { /* np. logowanie, następnie: */ throw; } }
  • 79.
    Izolacja transakcji • Domyślnie– Unspecified – Wykorzystywany poziom transakcji klienta • Jeśli inny – klient musi mied zadeklarowany poziom transakcji taki sam (inaczej błąd) class MyService : IMyContract { ... } [ServiceBehavior(TransactionIsolationLevel = IsolationLevel.Unspecified)] class MyService : IMyContract { ... }
  • 80.
    Klient using (TransactionScope scope= new TransactionScope()) { MyContractClient proxy1 = new MyContractClient( ); proxy1.MyMethod( ); proxy1.Close( ); MyOtherContractClient proxy2 = new MyOtherContractClient( ); proxy2.MyOtherMethod( ); proxy2.Close( ); scope.Complete( ); }
  • 81.
    Instancjonowanie PerCall MyContractClient proxy= new MyContractClient( ); using(TransactionScope scope = new TransactionScope( )) { proxy.MyMethod( ... ) ; proxy.MyMethod( ... ) ; scope.Complete( ); } proxy.Close( );
  • 82.
    Instancjonowanie PerSession • Domyślnie– podobnie jak PerCall – Po scope.Complete – instancja usuwana – ReleaseServiceInstanceOnTransactionComplete – ConcurrencyMode musi byd Single [ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = true)] class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod( ) { ... } [OperationBehavior( ... )] public void MyOtherMethod( ) { ... } }
  • 83.
    „Prawdziwe” PerSession • ReleaseServiceInstanceOnTransactionComplete= false • Zapisywanie stanu – Resource Manager – jako klucz identyfikator sesji OperationContext.Current.SessionId – Volatile Resource Managers – pola obiektu instancji
  • 84.
    PerSession – c.d. •Możliwe zrównanie czasu trwania sesji z czasem trwania transakcji – ReleaseServiceInstanceOnTransactionComplete = false – TransactionAutoComplete = false – Uwaga na timeout • Nie zalecane [ ServiceBehavior(TransactionAutoCompleteOnSessionClose = true) ] class MyService : IMyContract {. . . }
  • 85.
    Durable Services • Stanzapisywany w bazie – Transakcja może niechcący przed tym powstrzymad • Domyślnie – zapisywanie stanu nie jest częścią transakcji • [SaveStateInOperationTransaction] – Domyślnie – false – True – aby stan był zarządzany transakcją
  • 86.
    Singleton • Domyślnie –PerCall – ReleaseServiceInstanceOnTransactionComplete • Stan w Volatile Resource Managers
  • 87.
  • 88.
    Tryby • [ServiceBehavior(ConcurrencyMode=…)+ – Single –Reentrant – Multiple • Single – Domyślny – Tylko jeden wątek na raz • Multiple – Konieczne synchronizowanie dostępu • lock { } • [MethodImpl(MethodImplOptions.Synchronized)] • W takim wypadku – niewielki zysk – Limitowane przez throttle – domyślnie 16 połączeo • Reentrant
  • 89.
    Wątek UI • WPF,Silverlight – Dispatcher partial class MyForm : Form { Label m_CounterLabel; public SynchronizationContext MySynchronizationContext { get; set; } public MyForm() { InitializeComponent(); MySynchronizationContext = SynchronizationContext.Current; } void InitializeComponent() { . . . m_CounterLabel = new Label( ); . . . } public int Counter { get { return Convert.ToInt32(m_CounterLabel.Text); } set { m_CounterLabel.Text = value.ToString(); } } }
  • 90.
    Synchronization Context –c.d. [ServiceContract] interface IFormManager { [OperationContract] void IncrementLabel(); } class MyService : IFormManager { public void IncrementLabel() { MyForm form = Application.OpenForms[0] as MyForm; Debug. Assert(form ! = null) ; SendOrPostCallback callback = delegate { form.Counter++; }; form.MySynchronizationContext.Send(callback, null); } } static class Program { static void Main() { ServiceHost host = new ServiceHost(typeof(MyService)); host.Open(); Application.Run(new MyForm()); host.Close(); } }
  • 91.
    Wątek UI [ServiceBehavior(UseSynchronizationContext =true)] class MyService : IMyContract {. . . } • Jeśli wątek uruchamiający usługę ma Synchronization Context, automatycznie kontekst przechodzi do usługi • Form jako usługa (i tak ścisłe powiązanie) – Form jako callback – ok • Inne rozszerzenia – Np. Ustawianie priorytetów dla wywołao http://msdn.microsoft.com/en-us/magazine/cc163321.aspx
  • 92.
    Operacje asynchroniczne • Specjalnepole checkbox podczas dodawania referencji • Klasycznie jak w .NET – Begin<operacja>, End<operacja> – End – blokuje – Callback – Polling – IsCompleted – Wiele wywołao – WaitAll() i IAsyncResult.WaitHandle
  • 93.
  • 94.
    Serwisy kolejkowane • Dostępnośdusługi • Load leveling – skolejkowanie „piku” • Kolejkowanie niezależnych operacji biznesowych • Kompensacja (druga kolejka z wynikami) • Kolejkowanie w WCF – NetMsmqBinding – Opakowanie komunikatu SOAP w komunikat MSMQ
  • 95.
    Architektura • Klient wywołujeproxy • Proxy zapisuje komunikat do kolejki • Usługa netMsmqBinding instaluje Channel Listener • Channel listener powiadamia o komunikacie – komunikat zdejmowany i przetwarzany
  • 96.
    Kolejkowanie w WCF •Tylko operacje OneWay – Nie mogą zwracad wartości – Nie mogą zwracad błędów • Kolejki MSMQ – Public – pomiędzy serwerami w domenie – Private – lokalne, nie wymagają DC – Jeden endpoint = jedna kolejka • Hosting WAS – nazwa kolejki = nazwa pliku svc – address = "net. msmq: //localhost/private/WASService. svc" <endpoint address = "net.msmq: //localhost/private/MyServiceQueue" binding = "netMsmqBinding" ... />
  • 97.
    Transakcje • MSMQ uczestniczyw transakcjach (opcja) – Jeśli włączone – komunikaty zapisywane na dysk – Transakcje playback – nasz kod • Publiczna kolejka i reliable messeging – Kolejka proxy – Automatyczne ponawianie wysłania komunikatu
  • 98.
    Sesja i MSMQ •SessionMode.Allowed lub SessionMode.NotAllowed – brak sesji – Każdy komunikat pojedynczo • SessionMode.Required – Sessiongram – pakowanie komunikatów w „paczkę” – Zachowana kolejnośd doręczenia
  • 99.
    Instancjonowanie PerCall //Klient using(TransactionScope scope= new TransactionScope( )) { MyContractClient proxy = new MyContractClient( ); proxy.MyMethod( ); //Komunikat wysłany proxy.MyMethod( ); //Komunikat wysłany proxy.Close( ); scope.Complete( ); } // Zatwierdzane • Brak przejścia transakcji na stronę usługi • Każde wywołanie osobno, osobne instancje
  • 100.
    Instancjonowanie PerSession using(TransactionScope scope= new TransactionScope( )) { MyContractClient proxy = new MyContractClient( ); proxy.MyMethod(); proxy.MyMethod(); proxy.Close( ); //Skomponowano komunikat, zapis. scope.Complete( ); //Musi być za proxy.Close!! } //Pojedynczy komunikat zapisany
  • 101.
    Instancjonowanie PerSession • Niemogą mied trwającej sesji – Podobnie do PerCall • Ale - wszystkie komunikaty do tej samej instancji
  • 102.
    Throttling • Po włączeniuusługi – wszystkie komunikaty na raz – Duża liczba instancji kontekstu – obciążenie • Umożliwia stopniowe przetwarzanie
  • 103.
    Kiedy pojawią sięproblemy • Błędy – Komunikacja – Bezpieczeostwo – Quota – … • Dead-letter queue (DLQ) – Problem z doręczeniem – Nieudany commit transakcji playback – Przetwarzanie kolejki jak zwykły serwis (kontrakt musi byd ten sam / dziedziczący)
  • 104.
    <! -- Clientside --> <system. serviceModel> <client> <endpoint address = "net. msmq: //localhost/private/MyServiceQueue" binding = "netMsmqBinding" bindingConfiguration = "MyCustomDLQ" contract = "IMyContract" /> </client> <bindings> <netMsmqBinding> <binding name = "MyCustomDLQ" deadLetterQueue = "Custom" customDeadLetterQueue = "net.msmq: //localhost/private/MyCustomDLQ"> </binding> </netMsmqBinding> </bindings> </system. serviceModel> <! -- DLQ service side --> <system. serviceModel> <services> <service name = "MyDLQService"> <endpoint address = "net.msmq://localhost/private/MyCustomDLQ" binding = "netMsmqBinding" contract = "IMyContract" /> </service> </services> </system. serviceModel>
  • 105.
    Komunikaty Poison • KomunikatyPoison – Nieustanne próby doręczenia – Zawsze błąd – Również usługa z polimorficznym kontraktem • Możliwe akcje – Drop – odrzucenie i potwierdzenie, że dostarczono – Reject – odrzucenie i NACK – Move – przeniesienie do odpowiedniej kolejki
  • 107.
    Popularne wzorce • Responseservice – Komunikaty mogą byd jednokierunkowe – Można zapisywad odpowiedzi w innej kolejce • HTTP Bridge – Kolejki – raczej intranet – Nie interoperacyjne – Mostek WS-HTTP
  • 108.
  • 109.
    Tryby • Transport – Negocjacja(protokół), zależy od protokołu – Weryfikacja integralności treści komunikatu – Akcelerowane przez karty sieciowe • Message – Treśd komunikatu szyfrowana • Mieszane – Mix - transport do integralności i szyfrowanie hasła – Oba – uwaga na wydajnośd
  • 110.
    Bindingi i trybybezpieczeostwa
  • 111.
    Intranet • Bindingi – NetTcpBinding –NetNamedPipeBinding – NetMsmqBinding • Transport security – None – Signed – tożsamośd nadawcy i integralnośd komunikatu – Encrypted and signed – dodatkowo szyfrowanie • Najczęściej – uwierzytelnienie Windows (domain / username / password) • Autoryzacja – grupy Windows <bindings> <netTcpBinding> <binding name = "TCPWindowsSecurity"> <security mode = "Transport"> <transport clientCredentialType = "Windows" protectionLevel = "EncryptAndSign" /> </security> </binding> </netTcpBinding> </bindings>
  • 112.
    Intranet – c.d. •ServiceSercurityContext.Current – IsAnonymous – PrimaryIdentity – WindowsIdentity • Thread.CurrentPrincipal : IPrincipal • PrincipalPermissionMode – UseWindowsGroups – UseAspNetRoles – None, Custom • Deklaratywne wymuszanie – [PrincipalPermission(SecurityAction.Demand, Role = „Manager”)+ • Impersonacja – Możliwa, ale niezalecana
  • 113.
    Internet • Bindingi – WSHttpBinding –WSDualHttpBinding • Message security – Szyfrowanie certyfikatem X509 • Certyfikat – Plik konfiguracyjny (więcej kłopotu ze zmianami) – Trusted People – zalecane • Tryb walidacji certyfikatu (certificateValidation) – PeerTrust – TrustedPeople – ChainTrust – zaufane root authority: Verisign / Thwart / … – PeerOrChainTrust <behavior name = "Internet"> <serviceCredentials> <serviceCertificate findValue = "MyServiceCert" storeLocation = "LocalMachine" storeName = "My" x509FindType = "FindBySubjectName" /> </serviceCredentials> </behavior>
  • 114.
    Uwierzytelnienie // Hasło (klasycznie) MyContractClientproxy = new MyContractClient( ); proxy.ClientCredentials.UserName.UserName = "MyUsername"; proxy.ClientCredentials.UserName.Password = "MyPassword"; proxy.MyMethod( ); proxy.Close( ); // Windows (raczej nie stosowane w Internet, chociaż możliwe) <behavior name = "UsernameWindows"> <serviceCredentials> <userNameAuthentication userNamePasswordValidationMode = "Windows"/> <serviceCertificate . . . /> </serviceCredentials> </behavior>
  • 115.
    Autoryzacja • Najprościej –Role Provider – Model znany z ASP.NET – Dowolne źródło danych (własna lub generowana baza) • Duplex – Odradzane, w prosty sposób autoryzacja niedostępna
  • 116.
    B2B • Nie współdzieląkont – Przedstawianie się certyfikatem X509 (klient) – Dozwoleni klienci instalowani w Trusted People • Szyfrowanie certyfikatem serwera • Najczęściej nie potrzebna autoryzacja – Niewielka liczba lub jeden klient – Możliwe wykorzystanie Role Providera – identyfikator certyfikatu jako username
  • 117.
  • 118.
    Programowanie Web w WCF REST,Get, Post, Delete,RSS (i HTTP)
  • 119.
    Terminologia • RSS –prosty format XML dla zestawu danych • ATOM – jak RSS, ale bardziej ustrukturalizowany • Syndykacja (Syndication) – nadzbiór RSS, ATOM, inne • AJAX – DHTML + JavaScript + asynchroniczne przetwarzanie • JSON – Format danych (tablicy) w JavaScript • REST – … • POX – plain old XML (bez koperty SOAP)
  • 120.
    REST • Protokół HTTPzostał zaprojektowany z 8 słowami • Low REST – API bazujące na GET & POST – Też tak działa przeglądarka • High REST sugeruje stosowanie 4 głównych słow – GET/PUT/DELETE/POST – CRUD dla Web • Wywoływanie usługi bez warstwy „komunikatu” czy „koperty” – …sam HTTP • Oparte na – URI do definowania endpoint (zasób) • Spacje & URITemplates – Polecenia HTTP definiują operacje • GET/PUT/DELETE/POST – Typy zawartości • XML , JSON, <mikroformaty>
  • 121.
    Aplikacje REST -atrybuty • WebGet: Atrybut do oznaczania operacji które są wołane przez HTTP GET • webHttpBinding: Binding dla usług REST • webServiceHost: Klasa do hostowania usług REST • webServiceHostFactory: Klasa do hostowania usług REST które nie wymagają pliku konfiguracyjnego [OperationContract] [WebGet(UriTemplate=“/WeatherMap/{country}/{zipcode}”)] Stream GetWeatherMap(String country, String zipcode);
  • 122.
    Developing REST Applications WCF& URI Spaces: URITemplate • QueryString syntax still available • URITemplate: formalism for binding URI structure to method parameters [OperationContract] [WebGet(UriTemplate=“/WeatherMap/{country}/{zipcode}”)] Stream GetWeatherMap(String country, String zipcode); http://myserver/WeatherMap/USA/98052 http://myserver/GetWeatherMap?country=USA&zipcode=98052
  • 123.
    Developing REST Applications WCF& Transfer: HTTP verbs support • WebInvoke: new attribute for making a method accept any non-GET verb – PUT, DELETE, POST... [OperationContract] [WebInvoke(METHOD=“PUT”)] WeatherReport UploadWeatherReport(WeatherReport theReport);
  • 124.
    Ręczne modelowanie URI •Pomoc: System.UriTemplate Uri address = new Uri(“http://localhost:2000”); UriTemplate template = new UriTemplate(“{artist}/{album}”); Uri boundUri = template.BindByPosition(address, “Northwind”, “Overdone”); UriTemplateMatch match = template.Match(address, boundUri); String bandName = match.BoundVariables[“artist”];
  • 125.
    URI w kontrakcieWCF • Składnia QueryString nadal dostępna • Typ UriTemplate mapuje na parametry – Jako atrybut albo jako oddzielny typ [OperationContract] [WebGet(UriTemplate=“/Image/{bandName}/{album}”)] Stream GetAlbumImage(String bandName, String album); [OperationContract] [WebGet(UriTemplate=“/Image?name={bandName})] Stream GetMainImage(String bandName);
  • 126.
    Kontrakt typu zobacz/ zrób [OperationContract] [WebGet(UriTemplate=“/Image/{bandName}/{album}”)] Stream GetAlbumImage(String bandName, String album); [OperationContract] [WebInvoke(METHOD=“PUT”)] // {PUT, POST, DELETE} void AddAlbum(AlbumInfo albumInfo);
  • 127.
    REST – zwracaniewartości • Typ zawartości zależy od: – Zachowanie enableWebScript (JSON) albo ustawione w klasie hostującej – Atrybutu Response.Format – Ręcznie: • WebOperationContext.Current.OutgoingResponse.Conte ntType [OperationContract] [WebGet(UriTemplate = "WeatherReport/{country}/{zipcode}/JSON", ResponseFormat=WebMessageFormat.Json)] WeatherReport GetWeatherReportWithTemplateJSON(string country, string zipcode);
  • 128.
    JSON – coto jest? • JavaScript Object Notation • Format do łączenia JavaScript i obiektów – Łatwiejsze niż XML • Użycie – ASP.NET AJAX – Skrypty JS – Dynamiczny kod… var data = {“temp” : 59, “descr” : “cloudy”}; document.write (“The weather is “ + data.descr);
  • 129.
    Konwencje związane zdanymi [OperationContract(Name=“TestOp”)] [WebInvoke(METHOD=“PUT”)] // {PUT, POST, DELETE} String[] AddAlbum(AlbumInfo albumInfo); PUT /albumservice/AddAlbum HTTP 1.1 HOST: contoso.com <albumInfo> <Name>Hysteria</Name> <RelDate>8/3/1987</RelDate> ... </albumInfo> •XML Namespace pochodzi z kontraktu •Nazwy parametrów z sygnatury operacji
  • 130.
    Zwrócenie rysunku wWCF Stream GetAlbumImage(String bandName, String album){ Stream stream; // obraz „skądś” // ustawiamy ContentType i zwracamy strumień WebOperationContext.Current.OutgoingResponse.ContentType = “image/jpeg”; return stream; }
  • 131.
    WCF i syndykacja Syndykacja:Gromadzenie danych z różnych źródeł w jednym miejscu (blogi, artykuły itp.) SyndicationFeed: pojęcie „feedu” niezależne od formatu SyndicationFeedFormatter<> [ServiceKnownType(typeof(Atom10FeedFormatter))] [ServiceKnownType(typeof(Rss20FeedFormatter))] [ServiceContract] interface IPictureSyndication { [OperationContract] [WebGet(UriTemplate=“Images/{format}")] SyndicationFeedFormatter<SyndicationFeed> GetImage(String format); }
  • 132.
    Kontekst bez protokołuWS=* • Konwersacja bazująca na kanale komunikacyjnym - problem • Konwencja wywołania – W tle • Binding: basicHttpContextBinding NetTcpContextBinding BasicHttpContextBinding • Behavior: – <persistenceProvider> • Ten sam co w WF  • Uwaga! Klient musi też byd świadomy kontekstu – WCF Service Host nie jest…