Partager via


Instancing Initialization

Cet exemple étend l'exemple Pooling en définissant une interface, IObjectControl, qui personnalise l'initialisation d'un objet en l'activant et en le désactivant. Le client appelle des méthodes qui retournent l'objet au pool et d'autres qui ne retournent pas l'objet au pool.

ms751409.note(fr-fr,VS.90).gifRemarque :
La procédure d'installation ainsi que les instructions de génération relatives à cet exemple figurent à la fin de cette rubrique.

Points d'extensibilité

La première étape de création d'une extension Windows Communication Foundation (WCF) consiste à déterminer le point d'extensibilité à utiliser. Dans WCF, le terme EndpointDispatcher fait référence à un composant runtime chargé de convertir des messages entrants en appels de méthode sur le service de l'utilisateur et de convertir les valeurs de retour de cette méthode en message sortant. Un service WCF crée un EndpointDispatcher pour chaque point de terminaison.

L'EndpointDispatcher permet l'extensibilité de l'étendue du point de terminaison (pour tous les messages reçus ou envoyés par le service) à l'aide de la classe EndpointDispatcher. Cette classe vous permet de personnaliser différentes propriétés qui contrôlent le comportement de l'EndpointDispatcher. Cet exemple se concentre sur la propriété InstanceProvider qui pointe sur l'objet qui fournit les instances de la classe de service.

IInstanceProvider

Dans WCF, l'EndpointDispatcher crée des instances d'une classe de service à l'aide d'un fournisseur d'instances qui implémente l'interface IInstanceProvider. Cette interface possède uniquement deux méthodes :

  • GetInstance : lorsqu'un message arrive, le répartiteur appelle la méthode GetInstance afin de créer une instance de la classe de service pour traiter le message. La fréquence des appels à cette méthode est déterminée par la propriété InstanceContextMode. Par exemple, si la propriété InstanceContextMode a la valeur System.ServiceModel.InstanceContextMode.PerCall, une nouvelle instance de la classe de service est créée pour traiter chaque message qui arrive, et GetInstance est donc appelée chaque fois qu'un message arrive.
  • ReleaseInstance : lorsque l'instance de service finit de traiter le message, l'EndpointDispatcher appelle la méthode ReleaseInstance. À l'instar de la méthode GetInstance, la fréquence des appels à cette méthode est déterminée par la propriété InstanceContextMode.

Pool d'objets

La classe ObjectPoolInstanceProvider contient l'implémentation pour le pool d'objets. Cette classe implémente l'interface IInstanceProvider pour interagir avec la couche de modèle de service. Lorsque l'EndpointDispatcher appelle la méthode GetInstance, au lieu de créer une instance, l'implémentation personnalisée recherche un objet existant dans un pool en mémoire. Si aucun n'est disponible, il est retourné. Sinon, ObjectPoolInstanceProvider vérifie si la propriété ActiveObjectsCount (nombre d'objets retournés depuis le pool) a atteint la taille de pool maximale. Si ce n'est pas le cas, une nouvelle instance est créée et retournée à l'appelant et ActiveObjectsCount est incrémenté par la suite. Sinon, une demande de création d'objet est mise en file d'attente pendant le laps de temps configuré. L'implémentation pour GetObjectFromThePool est présentée dans l'exemple de code suivant.

private object GetObjectFromThePool()
{
    bool didNotTimeout = 
       availableCount.WaitOne(creationTimeout, true);
    if(didNotTimeout)
    {
         object obj = null;
         lock (poolLock)
        {
             if (pool.Count != 0)
             {
                   obj = pool.Pop();
                   activeObjectsCount++;
             }
             else if (pool.Count == 0)
             {
                   if (activeObjectsCount < maxPoolSize)
                   {
                        obj = CreateNewPoolObject();
                        activeObjectsCount++;
                            
                        #if (DEBUG)
                        WritePoolMessage(
                             ResourceHelper.GetString("MsgNewObject"));
                       #endif
                   }                        
            }
           idleTimer.Stop();
      }
     // Call the Activate method if possible.
    if (obj is IObjectControl)
   {
         ((IObjectControl)obj).Activate();
   }
   return obj;
}
throw new TimeoutException(
ResourceHelper.GetString("ExObjectCreationTimeout"));
}

L'implémentation ReleaseInstance personnalisée ajoute de nouveau l'instance libérée au pool et décrémente la valeur ActiveObjectsCount. L'EndpointDispatcher peut appeler ces méthodes à partir de différents threads et, par conséquent, l'accès synchronisé aux membres au niveau de la classe dans la classe ObjectPoolInstanceProvider est requis.

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            lock (poolLock)
            {
                // Check whether the object can be pooled. 
                // Call the Deactivate method if possible.
                if (instance is IObjectControl)
                {
                    IObjectControl objectControl = (IObjectControl)instance;
                    objectControl.Deactivate();

                    if (objectControl.CanBePooled)
                    {
                        pool.Push(instance);

                        #if(DEBUG)
                        WritePoolMessage(
                            ResourceHelper.GetString("MsgObjectPooled"));
                        #endif                        
                    }
                    else
                    {
                        #if(DEBUG)
                        WritePoolMessage(
                            ResourceHelper.GetString("MsgObjectWasNotPooled"));
                        #endif
                    }
                }
                else
                {
                    pool.Push(instance);

                    #if(DEBUG)
                    WritePoolMessage(
                        ResourceHelper.GetString("MsgObjectPooled"));
                    #endif 
                }
                                
                activeObjectsCount--;

                if (activeObjectsCount == 0)
                {
                    idleTimer.Start();                     
                }
            }

            availableCount.Release(1);
        }

La méthode ReleaseInstance fournit une fonctionnalité d'initialisation du nettoyage. Normalement, le pool gère un nombre minimal d'objets pendant sa durée de vie. Toutefois, il peut y avoir des périodes d'utilisation excessive qui requièrent la création d'objets supplémentaires dans le pool afin d'atteindre la limite maximale spécifiée dans la configuration. Par la suite, lorsque le pool devient moins actif, ces objets en surplus peuvent devenir une surcharge supplémentaire. Par conséquent, lorsque le activeObjectsCount atteint zéro, une minuterie d'inactivité est démarrée, qui déclenche et effectue un cycle de nettoyage.

if (activeObjectsCount == 0)
{
    idleTimer.Start(); 
}

Les extensions de couche ServiceModel sont raccordées à l'aide des comportements suivants :

  • Comportements de service : permettent de personnaliser l'ensemble de l'exécution du service.
  • Comportements de point de terminaison : permettent de personnaliser un point de terminaison de service particulier, y compris l'EndpointDispatcher.
  • Comportements de contrat : permettent de personnaliser la classe ClientRuntime ou la classe DispatchRuntime sur le client ou le service respectivement.
  • Comportements d'opération : permettent de personnaliser la classe ClientOperation ou la classe DispatchOperation sur le client ou le service respectivement.

Dans le cadre d'une extension de pool d'objets, il est possible de créer un comportement de point de terminaison ou un comportement de service. Dans cet exemple, nous utilisons un comportement de service qui applique la capacité de mise en pool d'objets à chaque point de terminaison du service. Les comportements de service sont créés par l'implémentation de l'interface IServiceBehavior. Il existe plusieurs méthodes pour que le ServiceModel tienne compte des comportements personnalisés :

  • Utilisation d'un attribut personnalisé ;
  • L'ajouter de façon impérative à la collection de comportements de la description de service ;
  • Extension du fichier de configuration.

Cet exemple utilise un attribut personnalisé. Lorsque ServiceHost est généré, il examine les attributs utilisés dans la définition de type du service et ajoute les comportements disponibles à la collection de comportements de la description de service.

L'interface IServiceBehavior possède trois méthodes : Validate, AddBindingParameters, et ApplyDispatchBehavior. Ces méthodes sont appelées par WCF lorsque le ServiceHost est initialisé. System.ServiceModel.Description.IServiceBehavior.Validate(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase) est appelée en premier ; elle autorise l'inspection du service pour retrouver les éventuelles incohérences. System.ServiceModel.Description.IServiceBehavior.AddBindingParameters(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase,System.Collections.ObjectModel.Collection{System.ServiceModel.Description.ServiceEndpoint},System.ServiceModel.Channels.BindingParameterCollection) est appelée ensuite ; cette méthode est requise uniquement dans les scénarios très avancés. System.ServiceModel.Description.IServiceBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase) est appelée en dernier ; elle est responsable de la configuration de l'exécution. Les paramètres suivants sont passés dans System.ServiceModel.Description.IServiceBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase) :

  • Description : ce paramètre fournit la description de service pour le service entier. Il permet d'inspecter les données de description des points de terminaison, des contrats et des liaisons, et les autres données associées au service.
  • ServiceHostBase : ce paramètre fournit le ServiceHostBase en cours d'initialisation.

Dans l'implémentation IServiceBehavior personnalisée, une nouvelle instance du ObjectPoolInstanceProvider est instanciée et assignée à la propriété InstanceProvider dans chaque EndpointDispatcher joint au ServiceHostBase.

public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
    if (enabled)
    {
        // Create an instance of the ObjectPoolInstanceProvider.
        instanceProvider = new ObjectPoolInstanceProvider(description.ServiceType,
        maxPoolSize, minPoolSize, creationTimeout);

        // Assign our instance provider to Dispatch behavior in each 
        // endpoint.
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
             ChannelDispatcher cd = cdb as ChannelDispatcher;
             if (cd != null)
             {
                 foreach (EndpointDispatcher ed in cd.Endpoints)
                 {
                        ed.DispatchRuntime.InstanceProvider = instanceProvider;
                 }
             }
         }
     }
} 

Outre une implémentation IServiceBehavior, la classe ObjectPoolingAttribute possède plusieurs membres pour personnaliser le pool d'objets à l'aide des arguments d'attribut. Ces membres incluent MaxSize, MinSize, Enabled et CreationTimeout, pour faire correspondre le jeu de fonctionnalités de mise en pool d'objets fourni par .NET Enterprise Services.

Le comportement de pool d'objets peut maintenant être ajouté à un service WCF en annotant l'implémentation de service avec l'attribut ObjectPooling personnalisé récemment créé.

[ObjectPooling(MaxSize=1024, MinSize=10, CreationTimeout=30000]    
public class PoolService : IPoolService
{
  // …
}

Activation et désactivation du raccordement

L'objectif principal de la mise en pool d'objets est d'optimiser la création et l'initialisation relativement coûteuses des objets éphémères. Par conséquent, elle peut singulièrement améliorer les performances d'une application si elle est utilisée correctement. Étant donné que l'objet est retourné depuis le pool, le constructeur est appelé une seule fois. Toutefois, certaines applications requièrent un certain niveau de contrôle afin de pouvoir initialiser et nettoyer les ressources utilisées dans un contexte unique. Par exemple, un objet utilisé pour un ensemble de calculs peut réinitialiser ses champs privés avant de traiter le calcul suivant. Les Enterprise Services ont activé ce type d'initialisation spécifique au contexte en laissant le développeur d'objets substituer les méthodes Activate et Deactivate de la classe ServicedComponent de base.

Le pool d'objets appelle la méthode Activate juste avant de retourner l'objet depuis le pool. Deactivate est appelé lorsque l'objet est retourné au pool. La classe ServicedComponent de base a également une propriété boolean appelée CanBePooled, qui peut être utilisée pour notifier au pool que l'objet peut être davantage regroupé.

Pour reproduire ces fonctionnalités, l'exemple déclare une interface publique (IObjectControl) avec les membres susmentionnés. Puis cette interface est implémentée par les classes de service conçues pour assurer l'initialisation spécifique au contexte. L'implémentation IInstanceProvider doit être modifiée pour satisfaire ces spécifications. Maintenant, chaque fois que vous obtenez un objet en appelant la méthode GetInstance, vous devez vérifier si l'objet implémente IObjectControl. Si c'est le cas, vous devez appeler la méthode Activate de la manière qui convient.

if (obj is IObjectControl)
{
    ((IObjectControl)obj).Activate();
}

Lors du retour d'un objet au pool, un contrôle de la propriété CanBePooled est requis avant d'ajouter de nouveau l'objet au pool.

if (instance is IObjectControl)
{
    IObjectControl objectControl = (IObjectControl)instance;
    objectControl.Deactivate();
    if (objectControl.CanBePooled)
    {
       pool.Push(instance);
    }
}

Vu que le développeur de service peut décider si un objet peut être regroupé ou non, le nombre d'objets dans le pool à un moment donné peut être inférieur à la taille minimale. Par conséquent, vous devez vérifier si le nombre d'objets est inférieur au minimum et effectuer l'initialisation nécessaire dans la procédure de nettoyage.

// Remove the surplus objects.
if (pool.Count > minPoolSize)
{
  // Clean the surplus objects.
}                    
else if (pool.Count < minPoolSize)
{
  // Reinitialize the missing objects.
  while(pool.Count != minPoolSize)
  {
    pool.Push(CreateNewPoolObject());
  }
}

Lorsque vous exécutez l'exemple, les demandes et réponses de l'opération sont affichées 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

  1. Assurez-vous d'avoir effectué la procédure indiquée à la section Procédure d'installation unique pour les exemples Windows Communication Foundation.

  2. Pour générer la solution, suivez les instructions indiquées dans la rubrique Génération des exemples Windows Communication Foundation.

  3. Pour exécuter l'exemple dans une configuration à un ou plusieurs ordinateurs, suivez les instructions indiquées dans Exécution des exemples Windows Communication Foundation.

Send comments about this topic to Microsoft.
© 2007 Microsoft Corporation. All rights reserved.