Durée de vie personnalisée
L’exemple Durée de vie montre comment écrire une extension WCF (Windows Communication Foundation) afin de fournir des services de durée de vie personnalisés pour des instances de service WCF partagées.
Notes
La procédure d'installation ainsi que les instructions de génération relatives à cet exemple figurent en fin de rubrique.
Instanciation partagée
WCF propose plusieurs modes d’instanciation pour vos instances de service. Le mode d’instanciation partagée présenté dans cet article offre un moyen de partager une instance du service entre plusieurs canaux. Les clients peuvent contacter une méthode de fabrique dans le service et créer un canal pour démarrer la communication. L’extrait de code suivant montre comment une application cliente crée un canal vers une instance existante du service :
// 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"));
}
Contrairement aux autres modes d'instanciation, l'instanciation partagée a une façon unique de libérer des instances de service. Par défaut, lorsque tous les canaux sont fermés pour un InstanceContext, le runtime de service WCF vérifie si le service InstanceContextMode est configuré sur PerCall ou PerSession ; si c’est le cas, il libère l’instance et demande les ressources. Si un IInstanceContextProvider personnalisé est utilisé, WCF appelle la méthode IsIdle de l’implémentation du fournisseur avant de libérer l’instance. Si IsIdle retourne true
, l’instance est libérée ; autrement, l’implémentation d’IInstanceContextProvider est chargée de notifier l’état inactif au Dispatcher
à l’aide d’une méthode de rappel. Cette opération s’effectue en appelant la méthode NotifyIdle du fournisseur.
Cet exemple montre comment retarder la libération de l’InstanceContext avec un délai d’inactivité de 20 secondes.
Extension de l'InstanceContext
Dans WCF, InstanceContext est le lien entre l’instance du service et le Dispatcher
. WCF vous permet d’étendre ce composant d’exécution en ajoutant un nouvel état ou comportement à l’aide de son modèle d’objet extensible. Le modèle d’objet extensible est utilisé dans WCF pour étendre des classes d’exécution existantes avec de nouvelles fonctionnalités ou pour ajouter de nouvelles fonctionnalités d’état à un objet. Le modèle d'objet extensible contient trois interfaces : IExtensibleObject<T>, IExtension<T> et IExtensionCollection<T>.
L’interface IExtensibleObject<T> est implémentée par des objets pour autoriser des extensions qui personnalisent leurs fonctionnalités.
L’interface IExtension<T> est implémentée par des objets qui peuvent être des extensions de classes de type T
.
Pour finir, l’interface IExtensionCollection<T> est une collection d’implémentations d’IExtension<T> qui autorise la récupération d’une implémentation d’IExtension<T> en fonction du type.
Par conséquent, pour étendre l’InstanceContext, vous devez implémenter l’interface IExtension<T>. Dans cet exemple de projet, la classe CustomLeaseExtension
contient cette implémentation.
class CustomLeaseExtension : IExtension<InstanceContext>
{
}
L'interface IExtension<T> possède deux méthodes : Attach et Detach. Comme leur nom le laisse supposer, ces deux méthodes sont appelées lorsque le runtime attache l’extension à une instance de la classe InstanceContext et l’en détache. Dans cet exemple, la méthode Attach
est utilisée pour effectuer le suivi de l’objet InstanceContext qui appartient à l’instance actuelle de l’extension.
InstanceContext owner;
public void Attach(InstanceContext owner)
{
this.owner = owner;
}
En outre, vous devez ajouter l’implémentation dont l’extension a besoin pour fournir la prise en charge de la durée de vie étendue. Par conséquent, l’interface ICustomLease
est déclarée avec les méthodes voulues et est implémentée dans la classe CustomLeaseExtension
.
interface ICustomLease
{
bool IsIdle { get; }
InstanceContextIdleCallback Callback { get; set; }
}
class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}
Lorsque WCF appelle la méthode IsIdle dans l’implémentation d’IInstanceContextProvider, cet appel est routé vers la méthode IsIdle de CustomLeaseExtension
. CustomLeaseExtension
vérifie ensuite son état privé pour voir si l’InstanceContext est inactif. S’il l’est, elle retourne true
. Sinon, elle démarre un minuteur pour l'extension spécifiée de la durée de vie.
public bool IsIdle
{
get
{
lock (thisLock)
{
if (isIdle)
{
return true;
}
else
{
StartTimer();
return false;
}
}
}
}
Dans l’événement Elapsed
du minuteur, la fonction de rappel du répartiteur (Dispatcher) est appelée pour démarrer un autre cycle de nettoyage.
void idleTimer_Elapsed(object sender, ElapsedEventArgs args)
{
lock (thisLock)
{
StopTimer();
isIdle = true;
Utility.WriteMessageToConsole(
ResourceHelper.GetString("MsgLeaseExpired"));
callback(owner);
}
}
Il n’existe aucun moyen de renouveler le minuteur en cours d’exécution lorsqu’un nouveau message arrive pour l’instance qui passe à l’état inactif.
L'exemple implémente IInstanceContextProvider pour intercepter les appels à la méthode IsIdle et les router vers CustomLeaseExtension
. L'implémentation d'IInstanceContextProvider est contenue dans la classe CustomLifetimeLease
. La méthode IsIdle est appelée lorsque WCF est sur le point de libérer l’instance du service. Toutefois, il n’existe qu’une instance d’une implémentation d’ISharedSessionInstance
particulière dans la collection IInstanceContextProvider du ServiceBehavior. Cela signifie qu’il n’existe aucun moyen de savoir si l’InstanceContext est fermé au moment où WCF vérifie la méthode IsIdle. Par conséquent, cet exemple utilise le verrouillage de threads pour sérialiser les requêtes adressées à la méthode IsIdle.
Important
Le recours au verrouillage de threads n'est pas une approche recommandée, car la sérialisation peut considérablement affecter les performances de votre application.
Un champ membre privé est utilisé dans la classe CustomLifetimeLease
pour suivre l’état inactif et est retourné par la méthode IsIdle. Chaque fois que la méthode IsIdle est appelée, le champ isIdle
est retourné et réinitialisé à false
. L'affectation de false
à cette valeur est indispensable pour que le répartiteur appelle la méthode NotifyIdle.
public bool IsIdle(InstanceContext instanceContext)
{
get
{
lock (thisLock)
{
//...
bool idleCopy = isIdle;
isIdle = false;
return idleCopy;
}
}
}
Si la méthode IInstanceContextProvider.IsIdle retourne false
, le Dispatcher inscrit une fonction de rappel à l’aide de la méthode NotifyIdle. Cette méthode reçoit une référence à l’InstanceContext libéré. Par conséquent, l’exemple de code peut interroger l’extension de type ICustomLease
et vérifier la propriété ICustomLease.IsIdle
à l’état étendu.
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);
}
}
}
Avant que la propriété ICustomLease.IsIdle
ne soit vérifiée, la propriété Callback doit être définie, condition essentielle pour que CustomLeaseExtension
puisse indiquer au Dispatcher le moment où elle devient inactive. Si ICustomLease.IsIdle
retourne true
, le membre privé isIdle
reçoit simplement dans CustomLifetimeLease
la valeur true
et appelle la méthode de rappel. Étant donné que le code maintient un verrou, d’autres threads ne peuvent pas modifier la valeur de ce membre privé. Et la prochaine fois que le Dispatcher appelle l’IInstanceContextProvider.IsIdle, il retourne true
et laisse le répartiteur libérer l’instance.
Le travail préparatoire étant à présent terminé, l’extension personnalisée doit être raccordée au modèle de service. Pour raccorder l’implémentation de CustomLeaseExtension
à l’InstanceContext, WCF fournit l’interface IInstanceContextInitializer qui permet d’effectuer l’initialisation d’InstanceContext. Dans l’exemple, la classe CustomLeaseInitializer
implémente cette interface et ajoute une instance de CustomLeaseExtension
à la collection Extensions à partir de la seule initialisation de la méthode. Cette méthode est appelée par le répartiteur en initialisant l'InstanceContext.
public void InitializeInstanceContext(InstanceContext instanceContext,
System.ServiceModel.Channels.Message message, IContextChannel channel)
//...
IExtension<InstanceContext> customLeaseExtension =
new CustomLeaseExtension(timeout, headerId);
instanceContext.Extensions.Add(customLeaseExtension);
}
Pour finir, l’implémentation d’IInstanceContextProvider est raccordée au modèle de service à l’aide de l’implémentation d’IServiceBehavior. Cette implémentation est placée dans la classe CustomLeaseTimeAttribute
et dérive également de la classe de base Attribute pour exposer ce comportement en tant qu'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;
}
}
}
}
Ce comportement peut être ajouté à une classe de l'exemple de service en l'annotant avec l'attribut CustomLeaseTime
.
[CustomLeaseTime(Timeout = 20000)]
public class EchoService : IEchoService
{
//…
}
Lorsque vous exécutez l'exemple, les requêtes et réponses de l'opération s'affichent dans les fenêtres de console du service et du client. Appuyez sur ENTER dans chaque fenêtre de console pour arrêter le service et le client.
Pour configurer, générer et exécuter l'exemple
Vérifiez que vous avez effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.
Pour générer l’édition C# ou Visual Basic .NET de la solution, conformez-vous aux instructions figurant dans Building the Windows Communication Foundation Samples.
Pour exécuter l’échantillon dans une configuration à un ou plusieurs ordinateurs, conformez-vous aux instructions fournies dans Exécution des échantillons Windows Communication Foundation.