Anpassad livslängd

Lifetime-exemplet visar hur du skriver ett WCF-tillägg (Windows Communication Foundation) för att tillhandahålla anpassade livslängdstjänster för delade WCF-tjänstinstanser.

Anmärkning

Installationsproceduren och bygginstruktionerna för det här exemplet finns i slutet av den här artikeln.

Delad instancing

WCF erbjuder flera instancing-lägen för dina tjänstinstanser. Det delade instancingläget som beskrivs i den här artikeln ger ett sätt att dela en tjänstinstans mellan flera kanaler. Klienter kan kontakta en fabriksmetod i tjänsten och skapa en ny kanal för att starta kommunikationen. Följande kodfragment visar hur ett klientprogram skapar en ny kanal till en befintlig tjänstinstans:

// 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"));
}

Till skillnad från andra instansieringslägen har det delade instansieringsläget ett unikt sätt att frigöra tjänstinstanserna. När alla kanaler är stängda för en InstanceContextkontrollerar WCF-tjänsten som standard om tjänsten InstanceContextMode är konfigurerad till PerCall eller PerSession, och i så fall släpper instansen och gör anspråk på resurserna. När en anpassad IInstanceContextProvider används, anropar WCF IsIdle-metoden i leverantörsimplementeringen innan instansen släpps. Om IsIdle returnerar true frisläpps instansen, annars ansvarar implementeringen IInstanceContextProvider för att Dispatcher meddela om inaktivt tillstånd med hjälp av en återanropsmetod. Detta görs genom att anropa NotifyIdle providerns metod.

Det här exemplet visar hur du kan fördröja frigörandet av InstanceContext med en inaktivitetstimeout på 20 sekunder.

Utvidga InstanceContext

I WCF är InstanceContext länken mellan tjänstinstansen och Dispatcher. Med WCF kan du utöka den här körningskomponenten genom att lägga till nytt tillstånd eller beteende med hjälp av dess utökningsbara objektmönster. Det utökningsbara objektmönstret används i WCF för att antingen utöka befintliga körningsklasser med nya funktioner eller för att lägga till nya tillståndsfunktioner i ett objekt. Det finns tre gränssnitt i det utökningsbara objektmönstret: IExtensibleObject<T>, IExtension<T>och IExtensionCollection<T>.

Gränssnittet IExtensibleObject<T> implementeras av objekt för att tillåta tillägg som anpassar deras funktioner.

Gränssnittet IExtension<T> implementeras av objekt som kan vara tillägg av klasser av typen T.

Och slutligen är IExtensionCollection<T>-gränssnittet en samling av IExtension<T>-implementeringar som gör det möjligt att hämta en implementering av IExtension<T> utifrån dess typ.

För att utöka InstanceContextmåste du därför implementera IExtension<T> gränssnittet. I det här exempelprojektet CustomLeaseExtension innehåller klassen den här implementeringen.

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

Gränssnittet IExtension<T> har två metoder Attach och Detach. Som deras namn antyder anropas dessa två metoder när körningen ansluter och kopplar från tillägget till en instans av InstanceContext klassen. I det här exemplet används Attach metoden för att hålla reda på InstanceContext objektet som tillhör den aktuella instansen av tillägget.

InstanceContext owner;

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

Dessutom måste du lägga till den nödvändiga implementationen i tillägget för att tillhandahålla förlängd livslängdsupport. ICustomLease Därför deklareras gränssnittet med önskade metoder och implementeras i CustomLeaseExtension klassen.

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

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

När WCF anropar metoden IsIdle inom implementeringen IInstanceContextProvider, dirigeras detta anrop till metoden IsIdle för CustomLeaseExtension. CustomLeaseExtension kontrollerar sedan dess privata tillstånd för att se om InstanceContext är inaktivt. Om den är inaktiv returnerar true den. Annars startar den en timer för en angiven förlängd livslängd.

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

Vid timerns Elapsed-händelse anropas återuppringningsfunktionen i Dispatcher för att starta en ny rensningscykel.

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

Det går inte att förnya körtimern när ett nytt meddelande tas emot för instansen som flyttas till viloläge.

Exemplet implementerar IInstanceContextProvider för att fånga upp anropen till metoden IsIdle och dirigera dem till CustomLeaseExtension. Implementeringen IInstanceContextProvider finns i CustomLifetimeLease klassen. Metoden IsIdle anropas när WCF är på väg att släppa tjänstinstansen. Det finns dock bara en instans av en viss ISharedSessionInstance implementering i ServiceBehaviors IInstanceContextProvider samling. Det innebär att det inte finns något sätt att veta om InstanceContext är stängd vid den tidpunkt då WCF kontrollerar IsIdle metoden. Därför använder det här exemplet trådlåsning för att serialisera begäranden till IsIdle metoden.

Viktigt!

Att använda trådlåsning är inte en rekommenderad metod eftersom serialisering kan påverka programmets prestanda allvarligt.

Ett privat medlemsfält används i CustomLifetimeLease klassen för att spåra inaktivt tillstånd och returneras av IsIdle metoden. Varje gång IsIdle metoden anropas returneras fältet isIdle och återställs till false. Det är viktigt att ange det här värdet för false för att se till att Dispatcher anropar NotifyIdle metoden.

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

Om IInstanceContextProvider.IsIdle metoden returnerar false, registrerar Dispatcher en återanropsfunktion med hjälp av NotifyIdle metoden. Denna metod tar emot en referens till InstanceContext som frigörs. Därför kan exempelkoden köra frågor mot ICustomLease typtillägget och kontrollera ICustomLease.IsIdle egenskapen i utökat tillstånd.

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

Innan egenskapen ICustomLease.IsIdle kontrolleras måste återanropsegenskapen anges eftersom detta är viktigt för CustomLeaseExtension att meddela Dispatcher när den blir inaktiv. Om ICustomLease.IsIdle returnerar true är den isIdle privata medlemmen helt enkelt inställd på CustomLifetimeLease till true och anropar återanropsmetoden. Eftersom koden innehåller ett lås kan andra trådar inte ändra värdet för den här privata medlemmen. Och nästa gång Dispatcher anropar IInstanceContextProvider.IsIdlereturnerar true den och låter Dispatcher släppa instansen.

Nu när grunden för det anpassade tillägget har slutförts måste det anslutas till tjänstmodellen. För att ansluta implementeringen CustomLeaseExtension till InstanceContext tillhandahåller WCF det IInstanceContextInitializer gränssnittet för att utföra bootstrapping av InstanceContext. I exemplet CustomLeaseInitializer implementerar klassen det här gränssnittet och lägger till en instans av CustomLeaseExtension i Extensions samlingen från den enda metodinitieringen. Den här metoden anropas av Dispatcher när du initierar InstanceContext.

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

    //...

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

Slutligen kopplas implementeringen IInstanceContextProvider till tjänstmodellen med hjälp av implementeringen IServiceBehavior . Den här implementeringen placeras i CustomLeaseTimeAttribute klassen och härleds även från basklassen Attribute för att exponera det här beteendet som ett attribut.

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

Det här beteendet kan läggas till i en exempeltjänstklass genom att kommentera det med attributet CustomLeaseTime .

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

När du kör exemplet visas åtgärdsbegäranden och svar i både tjänst- och klientkonsolfönstren. Tryck på RETUR i varje konsolfönster för att stänga av tjänsten och klienten.

Så här konfigurerar du, skapar och kör exemplet

  1. Kontrollera att du har utfört One-Time installationsproceduren för Windows Communication Foundation-exempel.

  2. Om du vill skapa C# eller Visual Basic .NET-versionen av lösningen följer du anvisningarna i Skapa Windows Communication Foundation-exempel.

  3. Om du vill köra exemplet i en konfiguration med en eller flera datorer följer du anvisningarna i Köra Windows Communication Foundation-exempel.