Objetos extensíveis

O padrão de objeto extensível é usado para estender classes de runtime existentes com nova funcionalidade ou para adicionar um novo estado a um objeto. As extensões, anexadas a um dos objetos extensíveis, permitem comportamentos em estágios muito diferentes no processamento a fim de acessar o estado compartilhado e a funcionalidade anexada a um objeto extensível comum que podem acessar.

O padrão IExtensibleObject<T>

Há três interfaces no padrão de objeto extensível: IExtensibleObject<T>, IExtension<T>e IExtensionCollection<T>.

A interface IExtensibleObject<T> é implementada por tipos que permitem objetos IExtension<T> para personalizar sua funcionalidade.

Objetos extensíveis permitem a agregação dinâmica de objetos IExtension<T>. Os objetos IExtension<T> são caracterizados pela seguinte interface:

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

A restrição de tipo garante que as extensões só podem ser definidas para classes que são IExtensibleObject<T>. Attach e Detach fornecem uma notificação de agregação ou desagregação.

É válido que as implementações restrinjam quando podem ser adicionadas e removidas de um proprietário. Por exemplo, você pode não permitir a remoção inteiramente, não permitindo adicionar ou remover extensões quando o proprietário ou a extensão estiver em um determinado estado, não permitir a adição a vários proprietários simultaneamente ou permitir apenas uma única adição seguida por uma única remoção.

IExtension<T> não implica interações com outras interfaces gerenciadas padrão. Especificamente, o método IDisposable.Dispose no objeto proprietário normalmente não desanexa suas extensões.

Quando uma extensão é adicionada à coleção, Attach é chamada antes de entrar na coleção. Quando uma extensão é removida da coleção, Detach é chamada depois que ela é removida. Isso significa que (supondo a sincronização apropriada) uma extensão pode contar apenas com a localização na coleção enquanto ela está entre Attach e Detach.

O objeto passado para FindAll ou Find não precisa ser IExtension<T> (por exemplo, você pode passar qualquer objeto), mas a extensão retornada é um IExtension<T>.

Se nenhuma extensão na coleção for um IExtension<T>, Find retornará nulo e FindAll retornará uma coleção vazia. Se forem implementadas várias extensões IExtension<T>, Find retornará uma delas. O valor retornado de FindAll é um instantâneo.

Há dois cenários principais. O primeiro cenário usa a propriedade Extensions como um dicionário baseado em tipo para inserir o estado em um objeto para permitir que outro componente pesquise-a usando o tipo.

O segundo cenário usa as propriedades Attach e Detach para permitir que um objeto participe do comportamento personalizado, como registrar-se em eventos, observar transições de estado e assim por diante.

Uma interface IExtensionCollection<T> é uma coleção de objetos IExtension<T> que permitem a recuperação de IExtension<T> pelo seu tipo. IExtensionCollection<T>.Find retorna o objeto adicionado mais recentemente que é um IExtension<T> desse tipo.

Objetos extensíveis no Windows Communication Foundation

Há quatro objetos extensíveis no WCF (Windows Communication Foundation):

  • ServiceHostBase – Essa é a classe base para o host do serviço. As extensões dessa classe podem ser usadas para estender o comportamento do próprio ServiceHostBase ou para armazenar o estado de cada serviço.

  • InstanceContext – Essa classe conecta uma instância do tipo do serviço com o runtime do serviço. Ela contém informações sobre a instância, bem como uma referência aos InstanceContexts que contêm ServiceHostBase. As extensões dessa classe podem ser usadas para estender o comportamento do InstanceContext ou armazenar o estado de cada serviço.

  • OperationContext – Essa classe representa as informações de operação que o runtime coleta para cada operação. Isso inclui informações como os cabeçalhos de mensagem de entrada, as propriedades da mensagem de entrada, a identidade de segurança de entrada e outras informações. As extensões dessa classe podem estender o comportamento de OperationContext ou armazenar o estado para cada operação.

  • IContextChannel – Essa interface permite a inspeção de cada estado para os canais e proxies criados pelo runtime do WCF. As extensões dessa classe podem estender o comportamento de IClientChannel ou armazenar o estado para cada operação.

O exemplo de código a seguir mostra o uso de uma extensão simples para rastrear 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();
    }
  }
}

Confira também