Partager via


Durée de vie personnalisée

L'exemple Lifetime montre comment écrire une extension Windows Communication Foundation (WCF) pour fournir des services de gestion de durée de vie personnalisés pour les instances de service WCF partagées.

Remarque

La procédure d’installation et les instructions de génération de cet exemple se trouvent à la fin de cet article.

Instanciation partagée

WCF propose plusieurs modes d’instanciation pour vos instances de service. Le mode d’instanciation partagé abordé dans cet article permet de partager une instance de 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 de service existante :

// 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 à d’autres modes d’instanciation, le mode d’instanciation partagé a un moyen unique de libérer les instances de service. Par défaut, lorsque tous les canaux sont fermés pour un InstanceContext, le runtime du service WCF vérifie si le service InstanceContextMode est configuré pour PerCall ou PerSession et, le cas échéant, libère l'instance et libère 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 renvoie true, l'instance est libérée ; sinon, l'implémentation IInstanceContextProvider est responsable de notifier l'état inactif au Dispatcher en utilisant une méthode de rappel. Pour ce faire, appelez la NotifyIdle méthode du fournisseur.

Cet exemple montre comment retarder la libération du InstanceContext avec un délai d’inactivité de 20 secondes.

Extension de l’instanceContext

Dans WCF, InstanceContext est le lien entre l’instance de service et le Dispatcher. WCF vous permet d’étendre ce composant runtime en ajoutant un nouvel état ou un comportement à l’aide de son modèle d’objet extensible. Le modèle d’objet extensible est utilisé dans WCF pour étendre des classes runtime existantes avec de nouvelles fonctionnalités ou pour ajouter de nouvelles fonctionnalités d’état à un objet. Il existe trois interfaces dans le modèle d’objet extensible : IExtensibleObject<T>, IExtension<T>et IExtensionCollection<T>.

L’interface IExtensibleObject<T> est implémentée par des objets pour autoriser les 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.

Enfin, l’interface IExtensionCollection<T> est une collection d’implémentations IExtension<T> qui permet de récupérer une implémentation de IExtension<T> en fonction de son type.

Par conséquent, pour étendre l’interface InstanceContext, vous devez implémenter l’interface IExtension<T> . Dans cet exemple de projet, la CustomLeaseExtension classe contient cette implémentation.

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

L’interface IExtension<T> a deux méthodes Attach et Detach. Comme leur nom l’implique, ces deux méthodes sont appelées lorsque le runtime attache et détache l’extension à une instance de la InstanceContext classe. Dans cet exemple, la Attach méthode 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 souhaitées et est implémentée dans la CustomLeaseExtension classe.

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

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

Lorsque WCF appelle la IsIdle méthode dans l’implémentation IInstanceContextProvider , cet appel est routé vers la IsIdle méthode du CustomLeaseExtension. Ensuite, le CustomLeaseExtension contrôle son état privé pour vérifier si le InstanceContext est inactif. S’il est inactif, il retourne true. Sinon, il démarre un minuteur pour une durée de vie prolongée spécifiée.

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 que l’instance soit déplacée vers l’état inactif.

L’exemple implémente IInstanceContextProvider pour intercepter les appels à la IsIdle méthode et les acheminer vers le CustomLeaseExtension. L’implémentation IInstanceContextProvider est contenue dans CustomLifetimeLease la classe. La IsIdle méthode est appelée lorsque WCF est sur le point de libérer l’instance de service. Toutefois, il n’existe qu’une seule instance d’une implémentation particulière ISharedSessionInstance dans la collection de IInstanceContextProvider ServiceBehavior. Cela signifie qu’il n’existe aucun moyen de savoir si l’objet InstanceContext est fermé au moment où WCF vérifie la IsIdle méthode. Par conséquent, cet exemple utilise le verrouillage de threads pour sérialiser les requêtes vers la IsIdle méthode.

Importante

L’utilisation du verrouillage de thread n’est pas une approche recommandée, car la sérialisation peut affecter gravement les performances de votre application.

Un champ membre privé est utilisé dans la CustomLifetimeLease classe pour suivre l’état inactif et est retourné par la IsIdle méthode. Chaque fois que la IsIdle méthode est appelée, le isIdle champ est retourné et réinitialisé à false. Il est essentiel de définir cette valeur false pour vous assurer que le répartiteur appelle la NotifyIdle méthode.

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

Si la méthode IInstanceContextProvider.IsIdle retourne false, le répartiteur 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 ICustomLease de type et vérifier la ICustomLease.IsIdle propriété dans 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 contient un verrou, d’autres threads ne peuvent pas modifier la valeur de ce membre privé. Et la prochaine fois que Dispatcher appelle le IInstanceContextProvider.IsIdle, il retourne true et permet à Dispatcher de libérer l’instance.

Maintenant que le travail de base de l’extension personnalisée est terminé, il doit être relié au modèle de service. Pour raccorder l’implémentation CustomLeaseExtension au InstanceContext, WCF fournit l’interface IInstanceContextInitializer pour effectuer l’amorçage de InstanceContext. Dans l’exemple, la CustomLeaseInitializer classe implémente cette interface et ajoute une instance de CustomLeaseExtension dans la collection Extensions à partir de la seule méthode d'initialisation. Cette méthode est appelée par Dispatcher lors de l’initialisation du InstanceContext.

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

    //...

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

Enfin, l’implémentation IInstanceContextProvider est connectée au modèle de service à l’aide de l’implémentation IServiceBehavior . Cette implémentation est placée dans la CustomLeaseTimeAttribute classe et dérive également de la Attribute classe de base 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é à un exemple de classe 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 d’opération s’affichent dans les fenêtres de console du service et de la console cliente. Appuyez sur Entrée dans chaque fenêtre de console pour arrêter le service et le client.

Pour configurer, générer et exécuter l’exemple

  1. Vérifiez que vous avez effectué la procédure d’installationOne-Time pour les exemples Windows Communication Foundation.

  2. 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.

  3. Pour exécuter l’exemple dans une configuration à un ou plusieurs ordinateurs, conformez-vous aux instructions figurant dans la rubrique Exécution des exemples Windows Communication Foundation.