Konkurs – vouchery na egzamin - pytania

Tak jak wczoraj pisaliśmy, dzisiaj publikujemy dwadzieścia pytań związanych z egzaminami, do których w ciągu ostatnich trzech miesięcy publikowaliśmy artykuły przygotowujące. Pytania są zamknięte i pierwsza osoba, która w komentarzu (przypominamy o poprawnym podaniu adresu email, na który wyślemy vouchery) poda poprawną odpowiedz na wszystkie pytania otrzyma nagrodę voucher na egzamin oraz voucher na kurs e-learningowy ASP.NET 3.5. Poniżej lista pytań:

70-562 – ASP:

  1. Masz DataSet zawierającego Customer DataTable oraz Order DataTable. Chcesz łatwo przechodzić z Order DataRow do Customer, który jest autorem zamówienia. Który obiekt umożliwi Ci łatwe przechodzenie z obiektów Order do obiektów Customer?

    A. DataColumn

    B. DataTable

    C. DataRow

    D. DataRelation

  2. Która z poniższych metod klasy HttpServerUtility może zostać użyta to przejścia na inną stronę aplikacji bez przesyłania o tym informacji do klienta:

    A. Redirect

    B. MapPath

    C. Transfer

    D. UrlDecode

  3. Potrzebujesz przechowywać dane, które są dostępne dla każdego użytkownika, który odwiedza Twoją stronę. Jakiej kolekcji powinieneś użyć w tym celu?

    A. Session

    B. Application

    C. Cookies

    D. ViewState

  4. Co musisz dodać do connection stringa, aby umożliwić dostęp do danych asynchroniczny?

    A. BeginExecute=true

    B. MultiThreaded=true

    C. MultipleActiveResultSets=true

    D. Asynchronous Processing=true

  5. Potrzebujesz generować dynamicznie dokumenty Worda, kiedy po aplikacji przychodzi żądanie pobrania pliku, którego rozszerzeniem jest .docx. Jak możesz to zrobić?

    A. Zaimplementować interfejs IPartitionResolver

    B. Zaimplementować interfejs IHttpModule

    C. Zaimplementować interfejs IHttpHandler

    D. Zaimplementować interfejs IHttpHandlerFactory

  6. Której klasy użyjesz podczas konwersji między typami danych .NET Framework a typami XML?

    A. XmlType

    B. XmlCast

    C. XmlConvert

    D. XmlSettings

  7. Tworzysz aplikację webową w ASP.NET w dziesiątkami stron wchodzących w jej skład. Chcesz zapisać preferencje użytkownika, tak aby można było się do tych informacji dostać z każdej strony. Dodatkowo chcesz, aby te ustawienia były zapamiętywany między kolejnymi wizytami użytkownika, nawet jak zamknie przeglądarkę. Który mechanizm zapamiętywania stanu po stronie klienta użyjesz?

    A. View state

    B. Control state

    C. Hidden fields

    D. Cookies

    E. Query strings

  8. Jaki tym uwierzytelniania na poziomie aplikacji musicie skonfigurować, aby móc skorzystać z domyślnego membership providera – AspNetSqlMembershipProvider?

    A. Windows

    B. Forms

    C. Passport

    D. None

  9. Potrzebujesz dynamicznie zmienić master page dla strony. W którym zdarzeniu strony to zrobisz?

    A. Page_Load

    B. Page_Render

    C. Page_PreRender

    D. Page_PreInit

  10. Chcesz napisać serwis WCF, który będzie hostowany przez IIS. Który typ projektu powinieneś użyć?

    A. WCF Service library

    B. WCF Service application

    C. ASP.NET Web Service application

    D. Windows Service

70-503 – WCF:

  1. Mamy klasę: 
    [ServiceBehavior()]
    public class ServiceImplementation : IServiceInterface
    {
        private int hitCounter;
        public void Increment()
        {
            hitCounter++;
        }
    }

    Jak należy udekorować klasę ServiceImplementation aby problemy współbieżności zostały wyeliminowane bez dodawania kodu w klasie? (Wybierz wszystkie poprawne odpowiedzi)

    A. ConcurrencyMode=Multiple i InstanceContextMode=Single

    B. ConcurrencyMode=Single i InstanceContextMode=PerSession

    C. ConcurrencyMode=Multiple i InstanceContextMode=PerSession

    D. ConcurrencyMode=Single i InstanceContextMode=Single

  2. Który z poniższych punktów rozszerzenia (po stronie serwisu) musi być też zaimplementowany po stronie klienta?

    A. Message Inspection

    B. Message Formatting

    C. Parameter Inspection

    D. Operation Invoker

  3. W obiekcie wiadomości właściwość MessageState jest ustawiona na Written. Które z poniższych zdarzeń zaszło?

    A. Metoda GetReaderAtBodyContents została wywołana.

    B. Metoda WriteBodyContents została wywołana.

    C. Metoda CreateBufferedCopy została wywołana.

    D. Metoda CreateMessage została wywołana.

  4. Właśnie definiujesz nowy kontrakt serwisu. Które z poniższych reprezentują atrybuty z przestrzeni System.ServiceModel, które na pewno będą potrzebne?

    A. ServiceContractAttribute i FaultContractAttribute

    B. OperationContractAttribute i FaultContractAttribute

    C. ServiceContractAttribute i OperationContractAttribute

    D. OperationContractAttribute i MessageParameterAttribute

  5. Będziesz obsługiwać serwis napisany w javie. Które z poniższych są poprawnymi metodami utworzenia proxy WCF do obsługi serwisu? (Wybierz wszystkie poprawne)

    A. Użyj klasy ChannelFactory class do utworzenia obiektu proxy dynamicznie.

    B. Użyj polecenia svcutil do utworzenia obieku proxy na podstawie definicji WSDL

    C. Ręcznie utwórz klasę proxy dziedziczącą po ClientBase.

    D. Dodaj referencję serwisu w Visual Studio (dodając referencję WSDL serwisu do projektu).

  6. Potrzebujesz uruchomić serwis na serwerze IIS. Która wersja IIS obsługuje protokoły nie-HTTP?

    A. IIS 5.1

    B. IIS 6.0

    C. IIS 7.0

    D. Żadna z wersji IIS

  7. Utworzyłeś aplikację kliencką WCF, która musi obsługiwać wywołanie zwrotne z serwisu. Który z bindingów użyjesz aby spełnić to wymaganie?

    A. basicHttpBinding

    B. wsHttpBinding

    C. wsHttpContextBinding

    D. wsDualHttpBinding

  8. Utworzyłeś aplikację kliencką WCF, która musi obsługiwać wywołanie zwrotne z serwisu. Który z poniższych bindingów NIE wspiera tego wymagania?

    A. netTcpBinding

    B. netNamedPipeBinding

    C. netMsmqBinding

    D. netTcpContextBinding

  9. Jaki będzie rezultat poniższego kodu?
    using (TransactionScope ts1 = new TransactionScope(
        TransactionScopeOption.Required, 
        new Timespan(0, 0, 30)))
    {
        using (TransactionScope ts2 = new TransactionScope(
            TransactionScopeOption.Required, 
            new Timespan(0, 0, 40)))
        {
            // Update database
            // Sleep for 35 seconds
            ts2.Complete();
        }
        ts1.Complete();
    }

    A. Obie transakcje zostaną zatwierdzone.

    B. Obie transakcje zostaną wycofane.

    C. Transakcja ts2 zostanie zatwierdzona a ts1 wycofana.

    D. Transakcja ts1 zostanie zatwierdzona, a ts2 wycofana.

  10. Masz aplikację Windows Forms. W pojedynczej transakcji Twoja aplikacja musi zaktualizować dwie bazy Microsoft SQL Server. Który koordynator transakcji zarządza tymi transakcjami?

    A. Lightweight Transaction Manager

    B. Microsoft Distributed Transaction Controller

    C. Kernel Transaction Manager

    D. Web Services Transaction Manager

Powodzenia!

Tagi: , , , , , ,

Konkurs – vouchery na egzamin

Tak jak zapowiadaliśmy wcześniej, mamy dla Was niespodziankę!

Mamy do rozdania kilka nagród:

  • 2 vouchery na dowolny egzamin (ważne do 30 lipca 2010)
  • 2 vouchery na kurs e-learningowy 6364: Visual Studio 2008 ASP.Net 3.5 (ważne do 30 czerwca 2010)

Chcieliśmy podziękować Olsztyńskiej Grupie IT za vouchery. Dzięki!!!

Konkurs na najlepszą “zajawkę”

Waszym zadaniem jest napisanie notki z linkami (70-536, 70-562, 70-503) do naszych kursów w jakimś blogu/serwisie. Najlepsza “zajawka” będzie nagrodzona: voucherem na egzamin, a kolejna voucherem na kurs e-learningowy.

Termin zgłaszania “zajawek” (termin wpisania linku do nich w komentarzach) godz. 12.00, poniedziałek 14 czerwca 2010 r!

Konkurs na najszybszego czytelnika

Jutro (piątek 11 czerwca 2010) o godzinie 12.00 pojawią się 20 pytania z zakresu przedstawianych przez nas kursów (dziesięć z WCF, dziesięć z ASP.NET).

Najszybsza osoba, która wpisze w komentarzu poprawne odpowiedzi na wszystkie pytania otrzyma voucher na egzamin i voucher na kurs e-learningowy.

Skrócony regulamin:
  • nagrody nie podlegają wymianie na ich wartość pieniężną
  • wszelkie konflikty rozwiązuje oraz “zajawki” ocenia komisja w składzie: Daniel Plawgo, Dawid Cieszyński, Kamil Lemański, Daniel Jarzynka, Dawid Tulski
  • członkowie komisji nie mogą brać udziału w konkursach
  • do komunikacji z zwycięzcami będzie użyty adres email pozostawiony w komentarzu

Tagi: , , , , , ,

70-503: Synchronization

Ten artykuł pochodzi z serii przygotowań do egzaminu 70-503: Windows Communication Foundation.

Ci z Was, którzy obsługiwali już wątki w .NET wiedzą, że nie jest to specjalnie skomplikowane. Najczęściej problemy występują przy obsłudze kontrolek Windows Forms, ponieważ ich właściwości mogą być zmieniane tylko w wątku, który je stworzył. Innym problemem jest wykorzystywanie lokalnej pamięci wątków do przechowywania informacji o kontekście, gdy proces nieoczekiwanie zmienia wątki, te dane mogą zniknąć. Z tej lekcji dowiemy się jak powyższe problemy są obsługiwane w WCFie.

Kontekst synchronizacji

W .NET 2.0 została wprowadzona rzadko używana funkcjonalność zwana kontekstem synchronizacji (klasa SynchronizationContext). Umożliwia ona sprawdzenie czy aktualnie wykonywany kod znajduje się w odpowiednim wątku. Aktualny kontekst możemy otrzymać odwołując się do statycznego pola SynchronizationContext.Current. Gdy jakaś metoda ma być wywołana w wątku, który nie jest bieżącym wątkiem, wywołujący wątek tworzy delegat typu SendOrPostCallback odwołujący się do żądanej metody a następnie jest przekazywany do metody Post (wywołanie asynchroniczne) lub Send (wywołanie synchroniczne) obiektu SynchronizationContext.

WCF i synchronizacja

A co ma kontekst synchronizacji do WCF’a? Nie wiem czy wiecie, że jeśli nie jest ustawione inaczej, każde wywołanie metod obiektu serwisu jest wykonywane przez wątki wejścia/wyjścia, z których żaden nie należy do naszej aplikacji. Gdybyśmy chcieli teraz zaktualizować coś w interfejsie użytkownika to napotkamy problem.

Z pomocą przychodzi nam właściwość UseSynchronizationContext klasy ServiceBehavior:

[ServiceBehavior(UseSynchronizationContext=true)]
public class UpdateService : IUpdateService

Ustawienie tego atrybutu na true spowoduje, że WCF będzie sprawdzał wątek uruchamiający hosta i jeśli wątek ten posiada kontekst synchronizacji i metoda serwisu jest wywołana z innego wątku, to będzie ona przekazywana do właściwego wątku i przez niego uruchomiona.

W przypadku hostowania serwisu w aplikacji Windows Forms/WPF, jeśli najpierw zostanie utworzony host przed oknem, nie zostanie utworzony żaden kontekst synchronizacji i każda aktualizacja kontrolek spowoduje błąd. Najpierw należy utworzyć okno a potem dopiero hosta serwisu.

Własny kontekst synchronizacji

WCF udostępnia tylko jedną klasę umożliwiającą obsługę kontekstu synchronizacji, dzięki której możemy np. aktualizować kontrolki w oknie aplikacji hostującej serwis. Możemy także utworzyć własne konteksty synchronizacji. Klasa kontekstu synchronizacjo odpowiada za wykonywanie metod serwisu przez konkretne wątki. Możemy wykorzystać ją np. do priorytetowania obsługi. Nasz kontekst synchronizacji może przekazywać wykonanie ważniejszych metod do wątków o wyższym priorytecie a pozostałych do innych wątków. Możemy właściwie zrobić o wiele więcej bazując na tym co udostępniają nam wątki.

Na początek potrzebna jest klasa bazująca na SynchronizationContext.

   1: public class ThreadPoolSynchronizer : SynchronizationContext, IDisposable
   2: {
   3:     Queue<WorkItem> workItemQueue;
   4:     WorkerThread[] workerThreads;
   5:     Semaphore itemAdded;
   6:  
   7:     public ThreadPoolSynchronizer(int poolSize)
   8:     {
   9:         if (poolSize <= 0)
  10:             throw new InvalidOperationException("Pool size cannot be zero");
  11:  
  12:         workItemQueue = new Queue<WorkItem>();
  13:  
  14:         workerThreads = new WorkerThread[poolSize];
  15:         for (int index = 0; index < poolSize; index++)
  16:             workerThreads[index] = new WorkerThread(index + 1, this);
  17:     }
  18:  
  19:     public void Close()
  20:     {
  21:         foreach (WorkerThread thread in workerThreads)
  22:             thread.Abort();
  23:     }
  24:  
  25:     public void Abort()
  26:     {
  27:         foreach (WorkerThread thread in workerThreads)
  28:             thread.Abort();
  29:     }
  30:  
  31:     public void Dispose()
  32:     {
  33:         this.Close();
  34:     }
  35:  
  36:     public override void Post(SendOrPostCallback method, Object state)
  37:     {
  38:         WorkItem workItem = new WorkItem(method, state);
  39:         QueueWorkItem(workItem);
  40:     }
  41:  
  42:     public override void Send(SendOrPostCallback method, Object state)
  43:     {
  44:         if (SynchronizationContext.Current == this)
  45:         {
  46:             method(state);
  47:             return;
  48:         }
  49:         WorkItem workItem = new WorkItem(method, state);
  50:         QueueWorkItem(workItem);
  51:         workItem.AsyncWaitHandle.WaitOne();
  52:     }
  53:  
  54:     protected Semaphore ItemAdded
  55:     {
  56:         get
  57:         {
  58:             if (itemAdded == null)
  59:                 itemAdded = new Semaphore(0, Int32.MaxValue);
  60:  
  61:             return itemAdded;
  62:         }
  63:         set
  64:         {
  65:             itemAdded = value;
  66:         }
  67:     }
  68:  
  69:     virtual internal void QueueWorkItem(WorkItem workItem)
  70:     {
  71:         lock (workItemQueue)
  72:         {
  73:             workItemQueue.Enqueue(workItem);
  74:             ItemAdded.Release();
  75:         }
  76:     }
  77:  
  78:     protected virtual bool QueueEmpty
  79:     {
  80:         get
  81:         {
  82:             lock (workItemQueue)
  83:             {
  84:                 if (workItemQueue.Count > 0)
  85:                 {
  86:                     return false;
  87:                 }
  88:                 return true;
  89:             }
  90:         }
  91:     }
  92:     internal virtual WorkItem GetNext()
  93:     {
  94:         ItemAdded.WaitOne(1000);
  95:         lock (workItemQueue)
  96:         {
  97:             if (workItemQueue.Count == 0)
  98:             {
  99:                 return null;
 100:             }
 101:             return workItemQueue.Dequeue();
 102:         }
 103:     }
 104:  
 105: }

Mamy tutaj prostą implementację własnej obsługi wątków. Dodatkowo utworzone są klasy WorkItem i WorkerThread (nazwy mówią same za siebie, poniżej pokażę ich kod).

Za funkcjonalność synchronizacji odpowiada pięć metod podzielonych na dwie grupy. Trzy metody operacyjne (Close, Abort, Dispose – linie 19-34) odpowiadają za zatrzymywanie wątku obsługującego żądanie. Dwie metody funkcyjne (Post i Send – linie 36-52) używane są przez WCF do uruchomienia metody. W metodzie Send (linie 44-48) sprawdzamy czy aktualny kontekst nie jest naszym kontekstem aby nie spowodować zakleszczenia.

Dla pełności przedstawiam jeszcze klasy WorkItem i WorkerThread:

   1:   [Serializable]
   2:   internal class WorkItem
   3:   {
   4:       object state;
   5:       SendOrPostCallback method;
   6:       ManualResetEvent asyncWaitHandle;
   7:  
   8:       public WaitHandle AsyncWaitHandle
   9:       {
  10:           get
  11:           {
  12:               return asyncWaitHandle;
  13:           }
  14:       }
  15:  
  16:       internal WorkItem(SendOrPostCallback method, object state)
  17:       {
  18:           this.method = method;
  19:           this.state = state;
  20:           asyncWaitHandle = new ManualResetEvent(false);
  21:       }
  22:  
  23:       internal void CallBack()
  24:       {
  25:           method(state);
  26:           asyncWaitHandle.Set();
  27:       }
  28:   }

 

 

 

   1: internal class WorkerThread
   2: {
   3:     ThreadPoolSynchronizer context;
   4:     public Thread threadObj;
   5:     bool endLoop;
   6:  
   7:     public int ManagedThreadId
   8:     {
   9:         get
  10:         {
  11:             return threadObj.ManagedThreadId;
  12:         }
  13:     }
  14:  
  15:     internal WorkerThread(int threadNumber, ThreadPoolSynchronizer context)
  16:     {
  17:         this.context = context;
  18:  
  19:         endLoop = false;
  20:         threadObj = null;
  21:  
  22:         threadObj = new Thread(Run);
  23:         threadObj.IsBackground = true;
  24:         threadObj.Name = "Tread-" + threadNumber.ToString();
  25:         threadObj.Start();
  26:     }
  27:  
  28:     bool EndLoop
  29:     {
  30:         set
  31:         {
  32:             lock (this)
  33:             {
  34:                 endLoop = value;
  35:             }
  36:         }
  37:         get
  38:         {
  39:             lock (this)
  40:             {
  41:                 return endLoop;
  42:             }
  43:         }
  44:     }
  45:  
  46:     void Start()
  47:     {
  48:         Debug.Assert(threadObj != null);
  49:         Debug.Assert(threadObj.IsAlive == false);
  50:         threadObj.Start();
  51:     }
  52:  
  53:     void Run()
  54:     {
  55:         Debug.Assert(SynchronizationContext.Current == null);
  56:         SynchronizationContext.SetSynchronizationContext(context);
  57:  
  58:         while (EndLoop == false)
  59:         {
  60:             WorkItem workItem = context.GetNext();
  61:             if (workItem != null)
  62:             {
  63:                 workItem.CallBack();
  64:             }
  65:         }
  66:     }
  67:  
  68:     public void Abort()
  69:     {
  70:         Debug.Assert(threadObj != null);
  71:         if (threadObj.IsAlive == false)
  72:         {
  73:             return;
  74:         }
  75:         EndLoop = true;
  76:  
  77:         threadObj.Join();
  78:     }
  79: }
  80:  

 

Teraz musimy przypisać nowy kontekst synchronizacji, może to wyglądać tak (przypisanie w linii 2):

   1: ThreadPoolSynchronizer syncContext = new ThreadPoolSynchronizer(3);
   2: SynchronizationContext.SetSynchronizationContext(syncContext);
   3: try
   4: {
   5:     ServiceHost host = new ServiceHost(typeof(UpdateService));
   6:     host.Open();
   7:     // Block until ready to quit
   8:     host.Close();
   9: }
  10: finally
  11: {
  12:     syncContext.Dispose();
  13: }

Innym lepszym sposobem jest udekorowanie klasy serwisu własnym atrybutem (tworzenie atrybutów wykracza poza ramy tego kursu). Może to wyglądać np. tak (atrybut z dwoma parametrami rozmiar puli oraz nazwa klasy serwisu):

[ThreadPoolSynchronization(3, typeof(UpdateService))]
[ServiceBehavior(typeof(IUpdateService))]
public class UpdateService : IUpdateService

Atrybut ten musi implementować interfejs IContractBehavior a w nim następujące metody:

  • AddBindingParameters – modyfikuje bindingi,
  • ApplyClientBehavior – modyfikuje lub rozszerza zachowanie serwisu dla wybranych lub wszystkich wiadomości,
  • ApplyDispatchBehavior - “wersja od strony serwisu” metody ApplyClientBehavior – rozszerza zachowanie dla przychodzących wiadomości,
  • Validate – potwierdza że kontrakt i punkt końcowy mogą obsłużyć zachowanie zaimplementowane w obiekcie.

Ponieważ chcemy zmienić  zachowanie po stronie serwisu, interesować nas będzie metoda AppliDispatchBehavior, która może być zaimplementowana np tak:

void ApplyDispatchBehavior(ContractDescription
description, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
    // ...
    if (dispatchRuntime.SynchronizationContext == null)
        dispatchRuntime.SynchronizationContext = new ThreadPoolSynchronizer(3);
    // ...
}

Synchronizacja i wywołania zwrotne

Obsługując wywołania zwrotne (ang. callback) także musimy brać pod uwagę problemy z współbieżnością. Podobnie jak w przypadku serwisu, u klienta możemy ustawić odpowiednie tryby synchronizacji, zarówno imperatywnie jak i deklaratywnie:

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Single)]
class CallbackClient : ICallback
{
    // Implementation code
}

Tak jak w ConcurrencyMode w klasie ServiceBehavior tak i tutaj mamy trzy możliwe wartości:

  • Single – tylko jedno wywołanie zwrotne jest możliwe w danym czasie, to gwarantuje nam że WCF nie wywoła metody więcej niż raz w tym samym czasie, nie gwarantuje natomiast że inne wątki klienta będą się odwoływać do zasobów używanych w tej metodzie, o to musimy sie już sami martwić;
  •  Multiple – dozwolone jest wielokrotne wywołanie metody, musimy sami postarać się o obsługę dostępu wielowątkowego;
  • Reentrant – metoda może być wywoływana ponownie przez serwis w tym samym wątku.

Wywołania zwrotne i konteksty synchronizacji

Możliwe jest także korzystanie z kontekstów synchronizacji w wywołaniach zwrotnych, wystarczy odpowiednio oznaczyć metodę:

[CallbackBehavior(UseSynchronizationContext=true)]
public class CallbackClient : ICallback
{
    // Implementation code
}

Na koniec jeszcze ważna informacja na temat wątków, wywołań zwrotnych i zakleszczeń. Załóżmy, że mamy przycisk uruchamiający metodę serwisu. Serwis dokonuje wywołania zwrotnego do aplikacji (dokładniej do wątku interfejsu użytkownika bo stamtąd pochodziło wywołanie serwisu). Wątek interfejsu użytkownika jest teraz zajęty bo czeka na odpowiedź od serwisu… No i mamy zakleszczenie. Jedynym rozwiązaniem tego problemu jest zrezygnowanie z kontekstu synchronizacji i ustawienie UseSynchronizationContext na false.

Na tym kończymy kurs.

Pozostańcie jeszcze z nami gdyż przygotowaliśmy małą niespodziankę.

Do zobaczenia na egzaminach :)

Tagi: , , , ,

70-503: Concurrency in WCF Applications

Ten artykuł pochodzi z serii przygotowań do egzaminu 70-503: Windows Communication Foundation.

Współbieżność (ang. concurrency) w serwisie WCF występuje, kiedy jednocześnie więcej niż jedno wywołanie ma miejsce. Celem serwisu WCF jest przetwarzanie przychodzących żądań. Kiedy żądanie przychodzi do serwisu, serwis rozdziela (ang. dispatch) komunikaty na własne wątki, które brane są z puli wątków. Z każdym żądaniem powiązany jest obiekt serwisu – instancja klasy, która implementuje interfejs serwisu. W WCF kwestia współbieżności zależy od tego, jak te obiekty są tworzone i dzielone pomiędzy pojedyncze żądania.

WCF przewiduje trzy możliwe tryby dzielenia obiektu serwisu:

  • Single – każdy wątek, który obsługuje żądanie może mieć dostęp do obiektu serwisu, ale tylko jeden w danym czasie, używanie trybu Single zmniejsza ilość problemów związanych z współbieżnością,
  • Reentrant – tylko jeden wątek może mieć dostęp do obiektu serwisu w danym czasie, jednak ma on możliwość opuszczenia obiektu i powrotu do niego w późniejszym czasie,
  • Multiple – obiekt serwisu obsługuje jednocześnie wiele żądań; jest to najtrudniejszy tryb do implementacji, ponieważ wymaga wielkiej staranności przy korzystaniu z zasobów dzielonych (ang. shared resources).

Tryb współbieżności jest ustawiany korzystając z atrybutu ServiceBehavior - ConcurrenczMode, na klasie, która implementuje serwis.

Tryb współbieżności Single

Ustawienie ConcurrencyMode na wartość ConcurrencyMode.Single gwarantuje najbardziej bezpieczne środowisko dla współbieżności.

   1: [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single)]
   2: public class ServiceImplementation : IServiceInterface
   3: {
   4:   // TODO: Implementacja...
   5: }

Przed rozpoczęciem przetwarzania żądania, na obiekt serwisu zakładana jest blokada, która zdejmowana jest dopiero po zakończeniu operacji. Jeżeli w tym czasie przyjdą kolejne żądania, zostaną one odłożone na kolejkę (FIFO – first-in, first-out) i czekają, kiedy obiekt serwisu będzie dostępny. Przetwarzanie pojedynczego żądania w danym czasie eliminuje problemy zarządzanie współbieżnością. Jedyną sytuacją, kiedy może pojawić się problem jest sytuacja, gdy obiekt serwisu wykonuje operacje wielowątkowe. Istnieje tu jednak pewien kompromis, którym zależy od trybu wystąpienia. Innymi słowy należy uwzględnić związek pomiędzy ConcurrencyMode a InstanceContextMode.

Przykład: Zakładamy, że mamy podaną gotową implementację serwisu. Jak możemy udekorować klasę, aby wyeliminować współbieżność, nie modyfikując niczego wewnątrz klasy?

   1: [ServiceBehavior()]
   2: public class ServiceImplementation : IServiceInterface
   3: {
   4:   private static int hitCounter;
   5:   public void Increment()
   6:   {
   7:     hitCounter++;
   8:   }
   9: }

Możemy ustawić ConcurrencyMode na Single a InstanceContextMode na PerSession lub Single. Wykorzystanie trybu współbieżności Single zapewnia, że tylko jedno żądanie może być przetwarzane w danej chwili. Połączenie ConcurrencyMode w trybie Single i InstanceContextMode w trybie Single daje gwarancję, że jest tworzona tylko jedna instancja serwisu. To połączenie jest wymagane w przypadku operacji ze statycznymi lub dzielonymi zmiennymi, aby były zabezpieczone na naruszenia współbieżności.

Tryb Single cechuje potencjalnie niska przepustowość spowodowana przesyłaniem przychodzących żądań przez jeden obiekt.

Tryb współbieżności Multiple

W przypadku serwisów, które wymagają większej przepustowości dostępny jest model wielowątkowy. Kiedy ConcurrencyMode jest ustawiony na Multiple, nie występuje już zakładanie blokady na obiekt serwisu przed obsłużeniem żądania, a obiekt serwisu może (w zależności od trybu wystąpienia) obsłużyć wiele żądań jednocześnie. Informacja o stanie serwisu i dzielonych zasobach musi być chroniona poprzez wykorzystanie standardowych technik synchronizacji, które oferuje .NET framework.

Tryb współbieżności Reentrant

Serwis po ustawienie trybu na Reentrant zachowuje się tak samo jak w przypadku trybu Single. Przed przetworzeniem żądania zakładana jest blokada na serwis i trzymana jest tak długo, jak długo trwają operacje. Różnica leży w tym, co może się wydarzyć podczas samego przetwarzania.

imagePodczas przetwarzania żądania przez serwis może wystąpić sytuacja, że serwis musi wykonać operację na innym serwisie. Inne żądania do serwisu muszą czekać na zakończenie aktualnie przetwarzanego przez serwis żądania, gdzie serwis czeka na zakończenie wywołania, które sam rozpoczął. Problem ten ilustruje zamieszczony rysunek.

Co się teraz stanie, jeżeli zewnętrzny serwis wywoła żądanie na naszym serwisie WCF? Żądanie zostanie dodane do kolejki, obiekt serwisu nie zostanie odblokowany, wystąpi zakleszczenie! (WCF jest w stanie poradzić sobie z tym przez unieważnienie żądanie po pewnym czasie – timeout, lub rzucenie wyjątku InvalidOperationException). Tryb Reentrant rozwiązuje ten problem.

Różnica między trybem Single a Reentrant jest taka, że w tym drugim trybie, kiedy obiekt serwisu wykonuje własne żądanie zdejmowana jest blokada. Pozwala to na obsłużenie innych żądań. Kiedy odpowiedz na żądanie serwisu nadejdzie, zostanie dodana do kolejki razem z innymi żądaniami. Kiedy zacznie się jego przetwarzanie, założy blokadę na obiekt serwisu i dokończy swoje zadanie.

Chociaż konfiguracja serwisu do działania w trybie reentrant jest prosta, programista musi musi liczyć się z dużą odpowiedzialnością wiążącą się z tym rozwiązaniem. Zakleszczenia są problemem, jednak nie jedynym. Programista musi zapewnić, że kiedy serwis wykonuje żądanie, stan serwisu musi pozostać spójny – serwis nie powinien oddziaływać ze swoimi własnymi polami (publicznymi, czy prywatnymi, należącymi do instancji, czy statycznymi) w taki sposób, aby jakikolwiek obiekt pozostał w nieakceptowanym stanie. Przykładowo, jeżeli budujemy strukturę drzewiastą, nie możemy dopuścić do tego, aby korzeń drzewa nie był zdefiniowany.

W następnej, ostatniej już lekcji, zapoznamy się z synchronizacją.

Tagi: , , , , ,

Znamy reprezentację na światowe finały Software Design

W środę odbył się etap krajowy konkursu Imagine Cup, kategorii Projektowanie Oprogramowania. Dzień wcześniej odbyła się sesja plakatowa, gdzie sędziowie mogli zapoznać się z każdym projektem i porozmawiać z członkami zespołów. Sędziowie wybrali 5 zespołów, które w ciągu 20 minut miały zaprezentować swoje pomysły.

Były to (w kolejności prezentowania):

  • eVolve z Uniwersytetu Adama Mickiewicza w Poznaniu – ich projekt reACT, umożliwiał przeprowadzanie terapii leczenia różnych rodzajów autyzmu i fobii przy wykorzystaniu wirtualnego środowiska, gdzie pacjent mógł poprzez swoje ruchy kooperować z tym co się dzieje w wirtualnym środowisku,
  • Mutant z Wojskowej Akademii Technicznej w Warszawie – PULSE, umożliwiał monitorowanie pracy serca, informujący użytkownika (oraz operatora – którego telefon miałby informować aby można było szybko wezwać pomoc) o zagrożeniach związanych z jego zdrowiem,
  • Fteams z Politechniki Łódzkiej – dzięki dwóm prostym kamerom internetowym stworzyli system interPeter, tłumaczący język migowy na język pisany i odwrotnie, ułatwiający w ten sposób komunikacje np. w urzędach, na poczcie itp.,
  • The Eradicators z Wojskowej Akademii Technicznej w Warszawie – parodiując serial “Dr House” zaprezentowali system Care2 umożliwiający modelowanie sieci społecznych i wykorzystanie ich do analizowania epidemii, wyszukiwania jednostek, których np. zaszczepienie spowoduje szybkie pokonanie choroby
  • Dreamcraft z Uniwersytetu Adama Mickiewicza w Poznaniu – na końcu zaprezentowany został projekt Soundability, służący do rozpoznawania dźwięków otoczenia (np. sygnałów alarmowych) i informujący o nich na ekranie telefonu oraz przez wibracje, miał służyć on np. osobom niedosłyszącym.

Miejsce pierwsze zajął zespół Fteams i to on pojedzie w lipcu do Warszawy na finały światowe, drugie miejsce zajął zespół MUTANT, trzecie The Eredicators. Czwarte miejsce zajęły razem zespoły eVolve i DreamCraft.

Wręczono także nagrody dodatkowe:

  • Nagroda dla najlepszego projektanta interfejsu dla Magdy Jonczyk z zespołu fteams,
  • Wyróżnienia w kategorii Internet Explorer 8 Award dla zespołów BlueLight, Cieszak Team (autor niniejszego artykułu), OneTeam, Rhea oraz Sampi Design,
  • Nagrody dla zespołów fteams i The Eradicators ufundowane przez firmę Helix Ventures.

Dodatkowo Ministerstwo Gospodarki zamierza nawiązać współpracę ze wszystkimi zespołami. Ministerstwo przygotowało kilka projektów, których zrealizowanie będzie ważne dla naszej gospodarki.

Wszystkim zespołom gratulujemy i życzymy dalszych sukcesów.

Tagi: ,

70-562: Using Caching to Improve Performance

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Często buforujemy dane na stronie bądź cała stronę co pozwala na szybszy dostęp do informacji niż z pliku czy bazy danych. Poprawia to oczywiście wydajność i skalowalność jeśli chodzi o liczbę użytkowników obsługiwanych na WWW. Nasz wspaniałomyślny ASP.NET bez większego nakładu pracy (czyt. pisania kodu) pozwala obsłużyć pamięć podręczną.  Wyróżniamy dwa rodzaje takiej pamięci:

Application caching- kolekcja ta może przechowywać dowolny obiekt, automatycznie zarządza pamięcią, limitami czasu przechowywania obiektu oraz innymi zależnościami.

Page output caching- pozwala przechowywać stronę, jej część lub wersje w pamięci co pozwala skrócić czas dostępu do niej.

Application Caching

Jest to proces przechowywania danych w którym mamy dostępny obiekt Cache który jest właściwością obiektu Page. Stanowi on zbiór klasy typu System.Web.Caching.Cache. Obiekt ten wykorzystuje całą pamięć podręczną co oznacza, że jest on jeden na cała aplikacje a nie na stronę. Poniższy rysunek pokazuje nam ten obiekt:

1

Praca z obiektem Cache jest bardzo podobna do pracy z sesją. Można przypisać element bezpośrednio nadając mu klucz i wartość. Przy pobieraniu wartość z pamięci podręcznej pamiętajmy aby sprawdzić czy nie jest ona null (mógł np. upłynąć czas przechowywania wartości). Poniższy przykład demonstruje jak pobrać obiekty string z pamięci:

   1: //C#
   2: Cache["Greeting"] = "Hello, world!";
   3:     if (Cache["Greeting"] != null)
   4:         value = (string)Cache["Greeting"];
   5:     else
   6:         value = "Hello, world!";

Oczywiście to jest najprostszy sposób i wydaje się mało rzeczywisty ale równie dobrze możemy przechowywać tam pliki, wyniki zapytań czy własne obiekty. Musimy tylko pamiętać aby zrzutować pobieraną wartość na odpowiedni typ.  W TK są opisane właściwości którymi możemy rozszerzyć o pewne właściwości nasze przechowywane dane i przykłady. Ja chciałbym przytoczyć jeden w którym jest ustawiany czas “trzymania” wartości ;)

   1: //C#
   2: Cache.Insert("FileCache", "CacheContents", null, DateTime.Now.AddMinutes(10),
   3: Cache.NoSlidingExpiration);

Page Output Caching

Często jest tak, że przeglądarka pobiera stronę, zapisuje ją na dysku i przy żądaniu sprawdza czy jest nowa wersja i jeżeli nie to ją wczytuje. Taki rozwiązanie m.in powoduje mniejsze obciążenie serwera. Aby zwiększyć wydajność i zmniejszyć czas renderowania, ASP.NET obsługuje tytułowy page output caching. Powoduje to, że np. serwer może trzymać w swojej pamięci zażądaną stronę i nawet kiedy inny użytkownik będzie chciał ją wczytać otrzyma ją w bardzo szybkim tempie. Jest to przydatne kiedy dana strona jest “ciężka”. Nie ma również problemu ze stornami tworzonymi dynamicznie wg. indywidualnych potrzeb użytkownika.

Możemy każdej ze stron ustawić buforowanie. Robimy to dodając do dyrektywy @ OutputCach. W TK są właściwości jakie możemy ustawić czyli np. czas, lokalizacje przechowywania oraz których nie możemy używać do kontrolek. Poniższy przykład pokazuje, jak ustawić “cachowanie” strony przez 15 minut, niezależnie od parametry przekazane do strony:

   1: <%@ OutputCache Duration="15" VaryByParam="location;count" %>

Nic nie stoi na przeszkodzie aby ustawić buforowanie dla całej aplikacji. Robimy sobie profile które potem możemy użyć na poszczególnych stronach. Oczywiście musimy umieścić odpowiednią sekcje w naszym web.config :

   1: <caching>
   2:     <outputCacheSettings>
   3:         <outputCacheProfiles>
   4:             <add name="OneMinuteProfile" enabled="true" duration="60"/>
   5:         </outputCacheProfiles>
   6:     </outputCacheSettings>
   7: </caching>

I aby nasza strona stosowała się do tego musimy przypisać jej ten profil:

   1: <%@ OutputCache CacheProfile="OneMinuteProfile" VaryByParam="none" %>

Na tym zakończę ten wpis. Wybaczcie, że jest on taki ogólnikowy ale jestem po nocnej podróży z Imagine Cup ;) W TK możecie doczytać sobie różne właściwości i np. jak sprawdzić czy skorzystać z zapisanej lokalnie strony czy nie ;) Jest to również ostatni wpis z serii przygotowań do egzaminu 70-562 ASP.NET. Wszystkim wiernym czytelnikom dziękuje w imieniu chłopaków jak i swoim :) Pamiętajcie, że artykuły nakreślają tematy zawarte w TK i mają tylko pomóc w zrozumieniu pewnych zagadnień a nie są kompendium wiedzy dotyczącej samego egzaminu i jego zawartości. Trzymajcie się, miłego weekendu! :D

Tagi: , , , ,

70-503: Programming Transactions

Ten artykuł pochodzi z serii przygotowań do egzaminu 70-503: Windows Communication Foundation.

No to wiemy już jak włączyć transakcje i co trzeba zrobić, zarówno po stronie serwisu jak i po stronie klienta, aby informacje o transakcji były przekazywane w obie strony. Dzisiaj dowiemy się więcej o obsłudze transakcji od strony kodu.

Transakcje otoczenia

W .NET Framework 2.0 w przestrzeni nazw System.Transaction zostały wprowadzone tzw. transakcje otoczenia (ang. Ambient Transactions). Polega to na tym, że transakcja istnieje w aktualnym wątku lub w kontekście obiektu i wszystkie działania w ramach tego wątku lub kontekstu wchodzą w skład tej transakcji (o ile taka transakcja została w ogóle rozpoczęta).

Aby sprawdzić czy mamy dostępną transakcję otoczenia sprawdzamy statyczne pole Current klasy Transaction:

Transaction ambientTransaction = Transaction.Current;

Jeśli nie ma transakcji, Transaction.Current jest równe null.

Jeśli nasz kod zostanie wywołany poprzez zdalnego klienta (przez binding w którym jest włączone przekazywanie transakcji) to ta lokalna transakcja stanie się transakcją rozproszoną (ang. distributed transaction).

Klasa Transaction (i obiekt Transaction.Current) udostępnia właściwość TransactionInformation, z której możemy dowiedzieć się jaka jest “lokalność” transakcji. Właściwość TransactionInformation zawiera dwa pola: LocalIdentifier – string zawierający unikalny identyfikator aktualnej transakcji, oraz DistributedIdentifier – string zawierający globalny unikalny identyfikator transakcji rozproszonej (GUID). Jeśli transakcja nie została podniesiona do rangi rozproszonej transakcji, wartość DistributedIdentifier będzie równa Guid.Empty.

Klasa TransactionScope

Jak sama nazwa wskazuje, klasa TransactionScope definiuje przestrzeń transakcji:

using (TransactionScope ts = new TransactionScope())
{
    //
    // tutaj aktualizujemy dane w systemie, itp.
    //
 
    ts.Complete();
}

W zależności od tego czy nasz kod jest wykonywany w ramach transakcji otoczenia (ang. ambient transaction) to, albo jest tworzona nowa transakcja LTM (jeśli nie ma transakcji otoczenia), albo to co robimy w ramach tej przestrzeni transakcji jest podłączane pod aktywną transakcję otoczenia.

Istotny jest sposób w jaki zakańczana jest transakcja. Transakcja istnieje dopóki istnieje obiekt klasy TransactionScope (dlatego jest ważne jest użycie using, lub jawne zwolnienie obiektu). Jeśli nie zwolnimy sami obiektu transakcji, może on nie być w ogóle zwolniony (czyli będzie aktywny przez całe działanie serwisu – o tym decyduje odśmiecacz (ang. garbage collector)), a nasza transakcja zakończy się niepowodzeniem przez przekroczenie czasu (ang. timeout).

Jeśli przed zwolnieniem obiektu klasy TransactionScope nie zostanie wywołana metoda Complete() całość transakcji zostanie wycofana (ang. rollback). Jeśli metoda Complete() zostanie wywołana, przy zwalnianiu obiektu cała transakcja zostanie zatwierdzona (ang. commit).

Głosowanie w transakcjach

Tak jak zostało powiedziane w poprzedniej lekcji, transakcje LTP charakteryzują się dwufazowym procesem zatwierdzania transakcji. Wywołanie metody Complete() obiektu TransactionScope nie gwarantuje zatwierdzenia transakcji, informuje ono tylko menadżera transakcji o tym, że u nas jest wszystko w porządku.

W przypadku gdy inny uczestnik transakcji zgłosi niepowodzenie, przy wywołaniu u nas metody Complete() dostaniemy wyjątek TransactionAbortedException. Tak więc obsługę transakcji musimy uzupełnić o obsługę tego wyjątku:

try
{
    using(TransactionScope ts = new TransactionScope( ))
    {
        /* Perform updates here */
        ts.Complete( );
    }
}
catch(TransactionAbortedException e)
{
    /* Rollback updates, if necessary */
}

Zagnieżdżanie transakcji

Domyślnie utworzenie nowej transakcji spowoduje podpięcie się pod aktualną transakcję, ewentualnie jeśli takiej nie ma to  utworzenie nowej. Jeden z konstruktorów klasy TransactionScope przyjmuje parametr typu TransactionScopeOption, który pozwala nam decydować czy chcemy utworzyć transakcję zagnieżdżoną czy dołączyć się do bieżącej transakcji, Wartości TransactionScopeOption:

  • Required – użyta jest transakcja otoczenia, lub stworzona nowa transakcja jeśli transakcja otoczenia nie istnieje, to jest wartość domyślna (tworząc obiekt klasy TransactionScope chcemy fragment kodu objąć transakcją, bez względu na to czy transakcja już istnieje czy nie),
  • RequiresNew – zawsze jest tworzona nowa transakcja i jest ona jednocześnie transakcją główną (ang. root transaction) dla wszystkich następnych,
  • Suppress – nawet jeśli istnieje transakcja otoczenia, żadne zmiany w tym fragmencie kodu nie zostaną uwzględnione w transakcji, ta opcja służy do wykonania kodu poza transakcją, wszelkie próby kontaktu z kodem działającym w transakcjach (np. wywołanie metody serwisu, która działa w transakcji spowoduje błąd)

Przykład zagnieżdżonych transakcji:

using(TransactionScope ts1 = new TransactionScope())
{
    using(TransactionScope ts2 = new TransactionScope())
    {
        ts2.Complete();
    }
    ts1.Complete();
}

Aby obie transakcje były zatwierdzone, metoda Complete() musi być wykonana dwukrotnie (raz dla każdego obiektu). Gdyby transakcja ts1 nie została zatwierdzona, żadne zmiany (nawet te zatwierdzone przez ts2) nie zostaną wprowadzone:

using(TransactionScope ts1 = new TransactionScope())
{
    using(TransactionScope ts2 = new TransactionScope())
    {
        ts2.Complete();
    }
}

Kolejnym zagadnieniem jest izolacja transakcji. Niektóre konstruktory klasy TransactionScope przyjmują jako parametr strukturę TransactionOptions, w której jedną z właściwości jest IsolationLevel:

  • Serializable – najwyższy poziom, inni użytkownicy nie widzą danych modyfikowanych w ramach transakcji, inne procesy nie mogę zmienić lub dodać danych będących w konflikcie z tą transakcją,
  • RepeatableRead – inne procesy mogą oglądać dane modyfikowane przez tą transakcję, ale te dane nie mogą być modyfikowane, ale mogą być dodane nowe dane które będą widoczne w transakcji,
  • ReadCommitted – inne procesy nie mogą oglądać niezatwierdzonych (ang. uncommited) danych,
  • ReadUncommitted – inne procesy mogą odczytywać i modyfikować niezatwierdzone dane,
  • Snapshot -  dane mogą być odczytywane, przed zatwierdzeniem transakcji sprawdzane jest czy w trakcje transakcji nie zostały zmienione dane (np. przez procesy zewnętrzne) poprzednio wczytane i modyfikowane w ramach transakcji
  • Chaos – zmiany z bardziej izolowanych transakcji nie mogą być zastępowane,
  • Unspecified – używany jest inny poziom izolacji, którego nie można określić, ustawienie takiej wartości spowoduje wyrzucenie wyjątku

Zagnieżdżone transakcje muszą używać tego samego poziomu izolacji jeśli chcą dołączyć do transakcji otoczenia (ang. ambient transaction), w przeciwnym przypadku wystąpi wyjątek ArgumentException.

Limity czasu transakcji

Możemy ustawić maksymalny czas trwania transakcji. Poniżej konstruktor z ustawionym czasem:

TransactionScope ts = new TransactionScope(TransactionScopeOption.Required,
    new TimeSpan(0, 10, 0));

Możemy też ustawić nieskończony czas trwania (co oczywiście jest dość ryzykowne):

TransactionScope ts = new TransactionScope(TransactionScopeOption.Required,
    TimeSpan.Zero);

Popatrzmy jeszcze na przykład zagnieżdżonych transakcji:

   1: using(TransactionScope ts1 = new
   2:     TransactionScope(TransactionScopeOption.Required,
   3:         new TimeSpan(0, 2, 0) ))
   4: {
   5:     // A transaction with a timespan of 2 minutes is created
   6:     using(TransactionScope ts2 = new
   7:         TransactionScope(TransactionScopeOption.Required,
   8:             new TimeSpan(0, 1, 0) ))
   9:     {
  10:         // A transaction with a timespan of 1 minute is created
  11:         Thread.Sleep(90000);
  12:     }
  13: }

Tutaj transakcja ts1 ma timeout 2 minuty (linia 3), a transakcja ts2: 1 minuta (linia 8). W transakcji ts2 występuje operacja (linia 11) trwająca dłużej niż 2 minuty i to transakcja ts2 spowoduje wycofanie całego bloku (włącznie z ts1).

Na koniec jeszcze informacja jak ustawić domyślny timeout:

<system.transactions>
    <defaultSettings timeout="00:00:10" />
</system.transactions>

W ostatnich lekcjach tego kursu powiemy odrobinę o współbieżności.

Tagi: , , , ,