Aangepaste levensduur
Het voorbeeld van de levensduur laat zien hoe u een WCF-extensie (Windows Communication Foundation) schrijft om aangepaste levensduurservices te bieden voor gedeelde WCF-service-exemplaren.
Notitie
De installatieprocedure en build-instructies voor dit voorbeeld bevinden zich aan het einde van dit artikel.
Gedeelde instancing
WCF biedt verschillende instancingmodi voor uw service-exemplaren. De modus voor gedeelde instancing die in dit artikel wordt behandeld, biedt een manier om een service-exemplaar tussen meerdere kanalen te delen. Clients kunnen contact opnemen met een factorymethode in de service en een nieuw kanaal maken om de communicatie te starten. In het volgende codefragment ziet u hoe een clienttoepassing een nieuw kanaal maakt voor een bestaand service-exemplaar:
// 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"));
}
In tegenstelling tot andere instancingmodi heeft de modus voor gedeelde instancing een unieke manier om de service-exemplaren vrij te geven. Wanneer alle kanalen zijn gesloten voor een InstanceContext, controleert de WCF-serviceruntime standaard of de service InstanceContextMode is geconfigureerd voor PerCall of PerSession, en als dat het geval is, wordt het exemplaar vrijgegeven en worden de resources geclaimd. Als een aangepaste IInstanceContextProvider toepassing wordt gebruikt, roept WCF de IsIdle methode van de implementatie van de provider aan voordat het exemplaar wordt vrijgegeven. Als IsIdle het exemplaar wordt true
geretourneerd, is de IInstanceContextProvider implementatie anders verantwoordelijk voor het melden van de Dispatcher
niet-actieve status met behulp van een callback-methode. Dit wordt gedaan door de NotifyIdle methode van de provider aan te roepen.
In dit voorbeeld ziet u hoe u het InstanceContext vrijgeven met een time-out voor inactiviteit van 20 seconden kunt vertragen.
De InstanceContext uitbreiden
In WCF InstanceContext is de koppeling tussen het service-exemplaar en het Dispatcher
. Met WCF kunt u dit runtime-onderdeel uitbreiden door nieuwe status of gedrag toe te voegen met behulp van het uitbreidbare objectpatroon. Het uitbreidbare objectpatroon wordt in WCF gebruikt om bestaande runtimeklassen uit te breiden met nieuwe functionaliteit of om nieuwe statusfuncties toe te voegen aan een object. Er zijn drie interfaces in het uitbreidbare objectpatroon: IExtensibleObject<T>, IExtension<T>en IExtensionCollection<T>.
De IExtensibleObject<T> interface wordt geïmplementeerd door objecten om extensies toe te staan die hun functionaliteit aanpassen.
De IExtension<T> interface wordt geïmplementeerd door objecten die uitbreidingen van klassen van het type T
kunnen zijn.
En ten slotte is de IExtensionCollection<T> interface een verzameling IExtension<T> implementaties waarmee een implementatie op basis van IExtension<T> hun type kan worden opgehaald.
Daarom moet u de interface implementeren om de InstanceContextIExtension<T> interface uit te breiden. In dit voorbeeldproject bevat de CustomLeaseExtension
klasse deze implementatie.
class CustomLeaseExtension : IExtension<InstanceContext>
{
}
De IExtension<T> interface heeft twee methoden Attach en Detach. Zoals hun namen impliceren, worden deze twee methoden aangeroepen wanneer de runtime de extensie koppelt en loskoppelt van een exemplaar van de InstanceContext klasse. In dit voorbeeld wordt de Attach
methode gebruikt om het InstanceContext object bij te houden dat deel uitmaakt van het huidige exemplaar van de extensie.
InstanceContext owner;
public void Attach(InstanceContext owner)
{
this.owner = owner;
}
Daarnaast moet u de benodigde implementatie toevoegen aan de extensie om de ondersteuning voor de verlengde levensduur te bieden. Daarom wordt de ICustomLease
interface gedeclareerd met de gewenste methoden en wordt deze geïmplementeerd in de CustomLeaseExtension
klasse.
interface ICustomLease
{
bool IsIdle { get; }
InstanceContextIdleCallback Callback { get; set; }
}
class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}
Wanneer WCF de IsIdle methode in de IInstanceContextProvider implementatie aanroept, wordt deze aanroep gerouteerd naar de IsIdle methode van de CustomLeaseExtension
. Vervolgens controleert de CustomLeaseExtension
privéstatus om te zien of de InstanceContext inactiviteit is. Als het niet actief is, wordt het geretourneerd true
. Anders wordt er een timer gestart voor een opgegeven hoeveelheid verlengde levensduur.
public bool IsIdle
{
get
{
lock (thisLock)
{
if (isIdle)
{
return true;
}
else
{
StartTimer();
return false;
}
}
}
}
In de gebeurtenis van Elapsed
de timer wordt de callbackfunctie in de Dispatcher aangeroepen om een andere opschooncyclus te starten.
void idleTimer_Elapsed(object sender, ElapsedEventArgs args)
{
lock (thisLock)
{
StopTimer();
isIdle = true;
Utility.WriteMessageToConsole(
ResourceHelper.GetString("MsgLeaseExpired"));
callback(owner);
}
}
Er is geen manier om de actieve timer te vernieuwen wanneer een nieuw bericht binnenkomt voor het exemplaar dat wordt verplaatst naar de niet-actieve status.
Het voorbeeld implementeert IInstanceContextProvider om de aanroepen naar de IsIdle methode te onderscheppen en deze door te leiden naar de CustomLeaseExtension
. De IInstanceContextProvider implementatie is opgenomen in CustomLifetimeLease
klasse. De IsIdle methode wordt aangeroepen wanneer WCF het service-exemplaar vrijgeeft. Er is echter slechts één exemplaar van een bepaalde ISharedSessionInstance
implementatie in de verzameling van IInstanceContextProvider ServiceBehavior. Dit betekent dat er geen manier is om te weten of de InstanceContext methode wordt gesloten op het moment dat WCF de IsIdle methode controleert. Daarom gebruikt dit voorbeeld threadvergrendeling om aanvragen naar de IsIdle methode te serialiseren.
Belangrijk
Het gebruik van threadvergrendeling is geen aanbevolen benadering omdat serialisatie de prestaties van uw toepassing ernstig kan beïnvloeden.
Een privélidveld wordt gebruikt in de CustomLifetimeLease
klasse om de niet-actieve status bij te houden en wordt geretourneerd door de IsIdle methode. Telkens wanneer de IsIdle methode wordt aangeroepen, wordt het isIdle
veld geretourneerd en opnieuw ingesteld op false
. Het is essentieel om deze waarde false
in te stellen om ervoor te zorgen dat de Dispatcher de NotifyIdle methode aanroept.
public bool IsIdle(InstanceContext instanceContext)
{
get
{
lock (thisLock)
{
//...
bool idleCopy = isIdle;
isIdle = false;
return idleCopy;
}
}
}
Als de IInstanceContextProvider.IsIdle methode retourneert false
, registreert de Dispatcher een callback-functie met behulp van de NotifyIdle methode. Deze methode ontvangt een verwijzing naar de InstanceContext release. Daarom kan de voorbeeldcode een query uitvoeren op de ICustomLease
typeextensie en de ICustomLease.IsIdle
eigenschap in de uitgebreide status controleren.
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);
}
}
}
Voordat de ICustomLease.IsIdle
eigenschap wordt gecontroleerd, moet de eigenschap Callback worden ingesteld omdat dit essentieel is om CustomLeaseExtension
de dispatcher op de hoogte te stellen wanneer deze niet actief wordt. Als ICustomLease.IsIdle
deze retourneert true
, wordt het isIdle
privélid gewoon ingesteld op CustomLifetimeLease
true
de callback-methode en wordt de callback-methode aangeroepen. Omdat de code een vergrendeling bevat, kunnen andere threads de waarde van dit privélid niet wijzigen. En de volgende keer dat Dispatcher de IInstanceContextProvider.IsIdleaanroept, wordt het geretourneerd true
en kan Dispatcher het exemplaar vrijgeven.
Nu het basiswerk voor de aangepaste extensie is voltooid, moet deze worden gekoppeld aan het servicemodel. Om de CustomLeaseExtension
implementatie aan de InstanceContextimplementatie te koppelen, biedt WCF de IInstanceContextInitializer interface voor het uitvoeren van de bootstrapping van InstanceContext. In het voorbeeld implementeert de CustomLeaseInitializer
klasse deze interface en voegt een exemplaar van de Extensions verzameling toe CustomLeaseExtension
vanuit de enige methode-initialisatie. Deze methode wordt aangeroepen door Dispatcher tijdens het initialiseren van de InstanceContext.
public void InitializeInstanceContext(InstanceContext instanceContext,
System.ServiceModel.Channels.Message message, IContextChannel channel)
//...
IExtension<InstanceContext> customLeaseExtension =
new CustomLeaseExtension(timeout, headerId);
instanceContext.Extensions.Add(customLeaseExtension);
}
Ten slotte wordt de IInstanceContextProvider implementatie gekoppeld aan het servicemodel met behulp van de IServiceBehavior implementatie. Deze implementatie wordt in de CustomLeaseTimeAttribute
klasse geplaatst en is ook afgeleid van de Attribute basisklasse om dit gedrag als kenmerk beschikbaar te maken.
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;
}
}
}
}
Dit gedrag kan worden toegevoegd aan een voorbeeldserviceklasse door er aantekeningen op te maken met het CustomLeaseTime
kenmerk.
[CustomLeaseTime(Timeout = 20000)]
public class EchoService : IEchoService
{
//…
}
Wanneer u het voorbeeld uitvoert, worden de bewerkingsaanvragen en -antwoorden weergegeven in zowel de service- als clientconsolevensters. Druk in elk consolevenster op Enter om de service en client af te sluiten.
Het voorbeeld instellen, compileren en uitvoeren
Zorg ervoor dat u de eenmalige installatieprocedure voor de Windows Communication Foundation-voorbeelden hebt uitgevoerd.
Als u de C# of Visual Basic .NET-editie van de oplossing wilt bouwen, volgt u de instructies in het bouwen van de Windows Communication Foundation-voorbeelden.
Als u het voorbeeld wilt uitvoeren in een configuratie met één of meerdere computers, volgt u de instructies in Het uitvoeren van de Windows Communication Foundation-voorbeelden.