Objetos extensibles

El patrón de objeto extensible se utiliza para extender clases de tiempo de ejecución existentes con nueva funcionalidad o para agregar un nuevo estado a un objeto. Las extensiones, asociadas a uno de los objetos extensibles, permiten que los comportamientos en fases muy diferentes de procesamiento tengan acceso al estado compartido y funcionalidad adjuntos a un objeto extensible común al que pueden tener acceso.

El patrón IExtensibleObject<T>

Hay tres interfaces en el patrón de objeto extensible: IExtensibleObject<T>, IExtension<T>y IExtensionCollection<T>.

Los tipos que permiten a los objetos IExtensibleObject<T> personalizar su funcionalidad implementan la interfaz IExtension<T>.

Los objetos extensibles permiten la agregación dinámica de objetos IExtension<T>. Los objetos IExtension<T> son caracterizados por la interfaz siguiente:

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

La restricción de tipo garantiza que las extensiones solo se pueden definir para las clases que son IExtensibleObject<T>. Attach y Detach proporcionan notificación de agregación o desagregación.

Es válido para que las implementaciones restrinjan cuando se pueden agregar y quitar de un propietario. Por ejemplo, puede impedir la eliminación por completo, impedir agregar o eliminar extensiones cuando el propietario o extensión están en un cierto estado, impedir agregar a varios propietarios simultáneamente o permitir solo una suma única seguida por una sola eliminación.

IExtension<T> no implica ninguna interacción con otras interfaces administradas estándar. En concreto, el método IDisposable.Dispose en el objeto de propietario no desasocia normalmente sus extensiones.

Cuando se agrega una extensión a la colección, se llama a Attach antes de que entre en la colección. Cuando se quita una extensión de la colección, se llama Detach después de quitarla. Esto significa (suponiendo que la sincronización sea correcta) que una excepción solo se puede encontrar en la colección mientras está entre Attach y Detach.

El objeto pasado a FindAll o Find no necesita ser IExtension<T> (por ejemplo, puede pasar cualquier objeto), pero la extensión que se devuelve es IExtension<T>.

Si ninguna extensión de la colección es IExtension<T>, Find devuelve NULL y FindAll devuelve una colección vacía. Si varias extensiones implementan IExtension<T>, Find devuelve una de ellas. El valor devuelto de FindAll es una captura.

Hay dos escenarios principales. El primer escenario utiliza la propiedad Extensions como un diccionario basado en tipos para insertar el estado en un objeto y permitir, de este modo, que otro componente lo pueda examinar utilizando el tipo.

El segundo escenario utiliza Attach y las propiedades Detach para permitir que un objeto participe en el comportamiento personalizado, como registrarse para los eventos, inspeccionar las transiciones de estado, etc.

La interfaz IExtensionCollection<T> es una colección de objetos IExtension<T> que permiten recuperar IExtension<T> por su tipo. IExtensionCollection<T>.Find devuelve el objeto agregado más recientemente que es una IExtension<T> de ese tipo.

Objetos extensibles en Windows Communication Foundation

Hay cuatro objetos extensibles en Windows Communication Foundation (WCF):

  • ServiceHostBase. Ésta es la clase base para el host del servicio. Las extensiones de esta clase se pueden utilizar para extender el comportamiento del ServiceHostBase o almacenar el estado para cada servicio.

  • InstanceContext. Esta clase conecta una instancia del tipo de servicio con el servicio en tiempo de ejecución. Contiene información sobre la instancia así como una referencia al InstanceContext que contiene ServiceHostBase. Las extensiones de esta clase se pueden utilizar para extender el comportamiento del InstanceContext o almacenar el estado para cada servicio.

  • OperationContext. Esta clase representa la información de la operación que el tiempo de ejecución reúne para cada operación. Esto incluye información como los encabezados de los mensajes entrantes, las propiedades de los mensajes entrantes, la identidad de seguridad de entrada y otra información. Las extensiones de esta clase pueden extender el comportamiento de OperationContext o almacenar el estado para cada operación.

  • IContextChannel: esta interfaz permite la inspección de cada estado en los canales y servidores proxy generados por el entorno de ejecución de WCF. Las extensiones de esta clase pueden extender el comportamiento de IClientChannel o utilizarlo para almacenar el estado de cada canal.

En el ejemplo de código siguiente se puede ver el uso de una extensión simple para seguir la pista de los objetos 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();
    }
  }
}

Consulte también