Udostępnij przez


Niestandardowy okres istnienia

W przykładzie Lifetime pokazano, jak napisać rozszerzenie programu Windows Communication Foundation (WCF) w celu zapewnienia niestandardowych usług okresu istnienia dla udostępnionych wystąpień usługi WCF.

Uwaga / Notatka

Procedura instalacji i instrukcje kompilacji dla tego przykładu znajdują się na końcu tego artykułu.

Udostępnianie instancingu

Program WCF oferuje kilka trybów instancjonowania dla instancji usługi. Tryb współdzielenia instancji opisany w tym artykule umożliwia współdzielenie wystąpienia usługi między wieloma kanałami. Klienci mogą skontaktować się z metodą fabryki w usłudze i utworzyć nowy kanał w celu rozpoczęcia komunikacji. Poniższy fragment kodu pokazuje, jak aplikacja kliencka tworzy nowy kanał do istniejącego wystąpienia usługi:

// Create a header for the shared instance id
MessageHeader shareableInstanceContextHeader = MessageHeader.CreateHeader(
        CustomHeader.HeaderName,
        CustomHeader.HeaderNamespace,
        Guid.NewGuid().ToString());

// Create the channel factory
ChannelFactory<IEchoService> channelFactory =
    new ChannelFactory<IEchoService>("echoservice");

// Create the first channel
IEchoService proxy = channelFactory.CreateChannel();

// Call an operation to create shared service instance
using (new OperationContextScope((IClientChannel)proxy))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy.Echo("Apple"));
}

((IChannel)proxy).Close();

// Create the second channel
IEchoService proxy2 = channelFactory.CreateChannel();

// Call an operation using the same header that will reuse the shared service instance
using (new OperationContextScope((IClientChannel)proxy2))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy2.Echo("Apple"));
}

W przeciwieństwie do innych trybów instancjonowania, wspólny tryb instancjonowania ma unikalną metodę zwalniania wystąpień usługi. Domyślnie, gdy wszystkie kanały są zamykane dla InstanceContext, środowisko uruchomieniowe usługi WCF sprawdza, czy usługa InstanceContextMode jest skonfigurowana na PerCall lub PerSession, a jeśli tak, zwalnia wystąpienie i przejmuje zasoby. Jeśli jest używany niestandardowy IInstanceContextProvider , program WCF wywołuje IsIdle metodę implementacji dostawcy przed udostępnieniem wystąpienia. Jeśli IsIdle zwraca true, wystąpienie jest zwalniane, w przeciwnym razie to implementacja IInstanceContextProvider jest odpowiedzialna za powiadamianie Dispatcher o stanie bezczynności przy użyciu metody wywołania zwrotnego. Odbywa się to przez wywołanie NotifyIdle metody dostawcy.

W tym przykładzie pokazano, jak można opóźnić zwolnienie InstanceContext z czasem bezczynności wynoszącym 20 sekund.

Rozszerzanie obiektu InstanceContext

W programie WCF InstanceContext jest połączeniem między wystąpieniem usługi a elementem Dispatcher. Program WCF umożliwia rozszerzenie tego składnika środowiska uruchomieniowego przez dodanie nowego stanu lub zachowania przy użyciu rozszerzalnego wzorca obiektu. Rozszerzalny wzorzec obiektu jest używany w programie WCF do rozszerzania istniejących klas środowiska uruchomieniowego o nowe funkcje lub dodawania nowych funkcji stanu do obiektu. Istnieją trzy interfejsy w rozszerzalnym wzorcu obiektu: IExtensibleObject<T>, IExtension<T>i IExtensionCollection<T>.

Interfejs IExtensibleObject<T> jest implementowany przez obiekty, aby umożliwić rozszerzenia, które dostosują ich funkcjonalność.

Interfejs IExtension<T> jest implementowany przez obiekty, które mogą być rozszerzeniami klas typu T.

I wreszcie, interfejs IExtensionCollection<T> jest kolekcją implementacji IExtension<T>, która umożliwia pobieranie implementacji IExtension<T> według ich typu.

W związku z tym, aby rozszerzyć element InstanceContext, należy zaimplementować IExtension<T> interfejs. W tym przykładowym projekcie CustomLeaseExtension klasa zawiera tę implementację.

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

Interfejs IExtension<T> ma dwie metody Attach i Detach. Jak implikują ich nazwy, te dwie metody są wywoływane, gdy środowisko uruchomieniowe dołącza i odłącza rozszerzenie do wystąpienia InstanceContext klasy. W tym przykładzie metoda Attach jest używana do śledzenia obiektu InstanceContext należącego do bieżącego wystąpienia rozszerzenia.

InstanceContext owner;

public void Attach(InstanceContext owner)
{
    this.owner = owner;
}

Ponadto należy dodać niezbędną implementację do rozszerzenia, aby zapewnić obsługę rozszerzonego okresu wsparcia. ICustomLease W związku z tym interfejs jest deklarowany przy użyciu żądanych metod i jest implementowany w CustomLeaseExtension klasie .

interface ICustomLease
{
    bool IsIdle { get; }
    InstanceContextIdleCallback Callback { get; set; }
}

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

Gdy WCF wywołuje metodę IsIdle w implementacji IInstanceContextProvider, to wywołanie jest kierowane do metody IsIdleCustomLeaseExtension. Następnie CustomLeaseExtension sprawdza swój stan prywatny, aby zobaczyć, czy InstanceContext jest bezczynny. Jeśli jest bezczynny, zwraca wartość true. W przeciwnym razie uruchamia czasomierz dla określonego wydłużonego czasu działania.

public bool IsIdle
{
  get
  {
    lock (thisLock)
    {
      if (isIdle)
      {
        return true;
      }
      else
      {
        StartTimer();
        return false;
      }
    }
  }
}

W zdarzeniu czasomierza Elapsed funkcja wywołania zwrotnego w dyspozytorze jest wywoływana w celu uruchomienia innego cyklu oczyszczania.

void idleTimer_Elapsed(object sender, ElapsedEventArgs args)
{
    lock (thisLock)
    {
        StopTimer();
        isIdle = true;
        Utility.WriteMessageToConsole(
            ResourceHelper.GetString("MsgLeaseExpired"));
        callback(owner);
    }
}

Nie można odnowić działającego timera, gdy nadejdzie nowa wiadomość dla wystąpienia, które zostaje przeniesione do stanu bezczynności.

Przykład implementuje IInstanceContextProvider, aby przechwytywać wywołania metody IsIdle i kierować je do CustomLeaseExtension. Implementacja IInstanceContextProvider jest zawarta w klasie CustomLifetimeLease. Metoda IsIdle jest wywoływana, gdy system WCF ma zwolnić instancję usługi. Jednak w kolekcji ServiceBehavior ISharedSessionInstance istnieje tylko jedno wystąpienie określonej IInstanceContextProvider implementacji. To oznacza, że nie można stwierdzić, czy InstanceContext jest zamknięty w momencie, gdy usługa WCF sprawdza metodę IsIdle. W związku z tym ten przykład używa blokowania wątków do serializacji żądań do metody IsIdle.

Ważne

Używanie blokowania wątków nie jest zalecanym podejściem, ponieważ serializacja może poważnie wpłynąć na wydajność aplikacji.

Prywatne pole składowe jest używane w CustomLifetimeLease klasie do śledzenia stanu bezczynności i jest zwracane przez metodę IsIdle . Za każdym razem, gdy metoda jest wywoływana IsIdle , isIdle pole jest zwracane i resetowane do false. Należy ustawić tę wartość na false, aby upewnić się, że dyspozytor wywołuje metodę NotifyIdle.

public bool IsIdle(InstanceContext instanceContext)
{
    get
    {
        lock (thisLock)
        {
            //...
            bool idleCopy = isIdle;
            isIdle = false;
            return idleCopy;
        }
    }
}

Jeśli metoda IInstanceContextProvider.IsIdle zwróci false, dyspozytor rejestruje funkcję wywołania zwrotnego przy użyciu metody NotifyIdle. Ta metoda otrzymuje odwołanie do InstanceContext będącego zwalnianym. W związku z tym przykładowy kod może wykonywać zapytania dotyczące ICustomLease rozszerzenia typu i sprawdzać właściwość ICustomLease.IsIdle w rozszerzonym stanie.

public void NotifyIdle(InstanceContextIdleCallback callback,
            InstanceContext instanceContext)
{
    lock (thisLock)
    {
       ICustomLease customLease =
           instanceContext.Extensions.Find<ICustomLease>();
       customLease.Callback = callback;
       isIdle = customLease.IsIdle;
       if (isIdle)
       {
             callback(instanceContext);
       }
    }
}

Przed sprawdzeniem właściwości ICustomLease.IsIdle należy ustawić właściwość Callback, ponieważ jest to niezbędne, żeby CustomLeaseExtension mógł powiadomić dyspozytora, gdy stanie się bezczynny. Jeśli ICustomLease.IsIdle zwraca true, prywatny element członkowski isIdle jest po prostu ustawiony w CustomLifetimeLease na true i wywołuje metodę wywołania zwrotnego. Ponieważ kod przechowuje blokadę, inne wątki nie mogą zmieniać wartości tego prywatnego pola. A następnym razem, gdy Dispatcher wywołuje IInstanceContextProvider.IsIdle, zwraca true i pozwala Dispatcherowi zwolnić wystąpienie.

Teraz, gdy przygotowano podstawy dla rozszerzenia niestandardowego, należy podłączyć ją do modelu usługi. Aby podłączyć implementację CustomLeaseExtension do InstanceContext, program WCF udostępnia interfejs IInstanceContextInitializer, umożliwiający inicjalizację InstanceContext. W przykładzie klasa CustomLeaseInitializer implementuje ten interfejs i dodaje instancję CustomLeaseExtension do kolekcji Extensions podczas inicjalizacji jedynej metody. Ta metoda jest wywoływana przez dyspozytor podczas inicjowania metody InstanceContext.

public void InitializeInstanceContext(InstanceContext instanceContext,
    System.ServiceModel.Channels.Message message, IContextChannel channel)

    //...

    IExtension<InstanceContext> customLeaseExtension =
        new CustomLeaseExtension(timeout, headerId);
    instanceContext.Extensions.Add(customLeaseExtension);
}

Na koniec implementacja IInstanceContextProvider jest podłączona do modelu usługi przy użyciu implementacji IServiceBehavior . Ta implementacja jest umieszczona w klasie CustomLeaseTimeAttribute i dodatkowo dziedziczy z klasy bazowej Attribute, aby uwidocznić to zachowanie jako atrybut.

public void ApplyDispatchBehavior(ServiceDescription description,
           ServiceHostBase serviceHostBase)
{
    CustomLifetimeLease customLease = new CustomLifetimeLease(timeout);

    foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
    {
        ChannelDispatcher cd = cdb as ChannelDispatcher;

        if (cd != null)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                ed.DispatchRuntime.InstanceContextProvider = customLease;
            }
        }
    }
}

To zachowanie można dodać do przykładowej klasy usługi, dodając do niej adnotacje za pomocą atrybutu CustomLeaseTime .

[CustomLeaseTime(Timeout = 20000)]
public class EchoService : IEchoService
{
  //…
}

Po uruchomieniu przykładu żądania operacji i odpowiedzi są wyświetlane w oknach konsoli usługi i klienta. Naciśnij ENTER w każdym oknie konsoli, aby zamknąć usługę i klienta.

Aby skonfigurować, skompilować i uruchomić przykładowy program

  1. Upewnij się, że wykonano procedurę konfiguracjiOne-Time dla przykładów programu Windows Communication Foundation.

  2. Aby skompilować wersję rozwiązania w języku C# lub Visual Basic .NET, postępuj zgodnie z instrukcjami w Kompilowanie przykładów Windows Communication Foundation.

  3. Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w Uruchamianie przykładów programu Windows Communication Foundation.