Objets extensibles

Le modèle d’objet extensible est utilisé pour étendre des classes d’exécution existantes à l’aide de nouvelles fonctionnalités ou ajouter un nouvel état à un objet. Les extensions, attachées à l’un des objets extensibles, permettent aux comportements à des étapes différentes du traitement, d’accéder à l’état partagé et aux fonctionnalités attachés à un objet extensible commun qui leur est accessible.

Modèle IExtensibleObject<T>

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 les types qui permettent aux objets IExtension<T> de personnaliser leurs fonctionnalités.

Les objets extensibles permettent le regroupement dynamique des objets IExtension<T>. Les objets IExtension<T> sont caractérisés par l'interface suivante :

public interface IExtension<T>
where T : IExtensibleObject<T>
{
    void Attach(T owner);
    void Detach(T owner);
}

La restriction de type garantit que les extensions ne peuvent être définies que pour les classes IExtensibleObject<T>. Attach et Detach fournissent une notification de l'agrégation ou de la désagrégation.

Il est valide pour les implémentations d'appliquer des restrictions lorsqu'elles peuvent être ajoutées et supprimées d'un propriétaire. Par exemple, vous pouvez complètement interdire la suppression en interdisant l'ajout ou la suppression des extensions lorsque le propriétaire ou l'extension est dans un certain état, interdire l'ajout simultané à plusieurs propriétaires ou autoriser uniquement un ajout unique suivi par une suppression unique.

IExtension<T> n'implique pas d'interactions avec d'autres interfaces standard managées. Plus précisément, la méthode IDisposable.Dispose sur l'objet de propriétaire ne détache pas normalement ses extensions.

Lorsqu’une extension est ajoutée à la collection, Attach est appelé avant d’entrer dans la collection. Lorsqu'une extension est supprimée de la collection, Detach est appelée après sa suppression. Cela signifie (en supposant une synchronisation correcte) qu'une extension ne peut figurer dans la collection que si elle se situe entre Attach et Detach.

L’objet passé à FindAll ou Find n’a pas besoin d’être IExtension<T> (par exemple, vous pouvez passer tout objet), mais l’extension retournée est un IExtension<T>.

Si aucune extension dans la collection est un IExtension<T>, Find retourne null, et FindAll retourne une collection vide. Si plusieurs extensions implémentent IExtension<T>, Find retourne l’une d’entre elle. La valeur retournée par FindAll est un instantané.

Il y a deux scénarios principaux. Le premier scénario utilise la propriété Extensions comme un dictionnaire basé sur un type pour insérer l'état sur un objet pour permettre à un autre composant de le rechercher à l'aide du type.

Le deuxième scénario utilise les propriétés Attach et Detach pour permettre à un objet de participer à un comportement personnalisé, tel que l'inscription aux événements, l'observation des transitions d'état, etc.

L'interface IExtensionCollection<T> est une collection d'objets IExtension<T> qui permet la récupération de IExtension<T> par son type. IExtensionCollection<T>.Find retourne l'objet récemment ajouté dans un IExtension<T> de ce type.

Objets extensibles dans Windows Communication Foundation

Windows Communication Foundation (WCF) comporte quatre objets extensibles :

  • ServiceHostBase - Il s'agit de la classe de base pour l'hôte du service. Les extensions de cette classe peuvent être utilisées pour étendre le comportement du ServiceHostBase lui-même ou pour stocker l'état pour chaque service.

  • InstanceContext - Cette classe connecte une instance du type du service à l'exécution du service. Elle contient des informations sur l'instance et une référence au InstanceContext qui contient ServiceHostBase. Les extensions de cette classe peuvent être utilisées pour étendre le comportement du InstanceContext ou pour stocker l'état pour chaque service.

  • OperationContext - Cette classe représente les informations d'opération que l'exécution rassemble pour chaque opération. Cela inclut des informations telles que les en-têtes de message entrant, les propriétés de message entrant, l'identité de sécurité entrante et d'autres informations. Les extensions de cette classe peuvent étendre le comportement de OperationContext ou stocker l’état pour chaque opération.

  • IContextChannel - Cette interface tient compte de l'inspection de chaque état pour les canaux et les proxys construits par l’exécution WCF. Les extensions de cette classe peuvent étendre le comportement de IClientChannel ou peuvent l’utiliser pour stocker l’état pour chaque canal.

L’exemple de code suivant montre l’utilisation d’une simple extension pour assurer le suivi des objets InstanceContext.

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.Text;

namespace Microsoft.WCF.Documentation
{
  public class MyInstanceContextInitializer : IInstanceContextInitializer
  {
    public void Initialize(InstanceContext instanceContext, Message message)
    {
      MyInstanceContextExtension extension = new MyInstanceContextExtension();

      //Add your custom InstanceContext extension that will let you associate state with this instancecontext
      instanceContext.Extensions.Add(extension);
    }
  }

  //Create an Extension that will attach to each InstanceContext and let it retrieve the Id or whatever state you want to associate
  public class MyInstanceContextExtension : IExtension<InstanceContext>
  {

    //Associate an Id with each Instance Created.
    String instanceId;

    public MyInstanceContextExtension()
    { this.instanceId = Guid.NewGuid().ToString(); }

    public String InstanceId
    {
      get
      { return this.instanceId; }
    }

    public void Attach(InstanceContext owner)
    {
      Console.WriteLine("Attached to new InstanceContext.");
    }

    public void Detach(InstanceContext owner)
    {
      Console.WriteLine("Detached from InstanceContext.");
    }
  }

  public class InstanceInitializerBehavior : IEndpointBehavior
  {

    public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters)
    {    }

    //Apply the custom IInstanceContextProvider to the EndpointDispatcher.DispatchRuntime
    public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
    {
      MyInstanceContextInitializer extension = new MyInstanceContextInitializer();
      endpointDispatcher.DispatchRuntime.InstanceContextInitializers.Add(extension);
    }

    public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)
    {    }

    public void Validate(ServiceEndpoint endpoint)
    {    }
  }

  public class InstanceInitializerBehaviorExtensionElement : BehaviorExtensionElement
  {

    public override Type BehaviorType
    {
      get { return typeof(InstanceInitializerBehavior); }
    }

    protected override object CreateBehavior()
    {
      return new InstanceInitializerBehavior();
    }
  }
}

Voir aussi