Extensible Objects

The extensible object pattern is used to either extend existing runtime classes with new functionality or to add new state to an object. Extensions, attached to one of the extensible objects, enable behaviors at very different stages in processing to access shared state and functionality attached to a common extensible object that they can access.

The IExtensibleObject<T> Pattern

There are three interfaces in the extensible object pattern: IExtensibleObject<T>, IExtension<T>, and IExtensionCollection<T>.

The IExtensibleObject<T> interface is implemented by types that allow IExtension<T> objects to customize their functionality.

Extensible objects allow dynamic aggregation of IExtension<T> objects. IExtension<T> objects are characterized by the following interface:

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

The type restriction guarantees that extensions can only be defined for classes that are IExtensibleObject<T>. Attach and Detach provide notification of aggregation or disaggregation.

It is valid for implementations to restrict when they may be added and removed from an owner. For example, you can disallow removal entirely, disallowing adding or removing extensions when the owner or extension are in a certain state, disallow adding to multiple owners concurrently, or allow only a single addition followed by a single remove.

IExtension<T> does not imply any interactions with other standard managed interfaces. Specifically, the IDisposable.Dispose method on the owner object does not normally detach its extensions.

When an extension is added to the collection, Attach is called before it goes into the collection. When an extension is removed from the collection, Detach is called after it is removed. This means (assuming appropriate synchronization) an extension can count on only being found in the collection while it is between Attach and Detach.

The object passed to FindAll or Find need not be IExtension<T> (for example, you can pass any object), but the returned extension is an IExtension<T>.

If no extension in the collection is an IExtension<T>, Find returns null, and FindAll returns an empty collection. If multiple extensions implement IExtension<T>, Find returns one of them. The value returned from FindAll is a snapshot.

There are two main scenarios. The first scenario uses the Extensions property as a type-based dictionary to insert state on an object to enable another component to look it up using the type.

The second scenario uses the Attach and Detach properties to enable an object to participate in custom behavior, such as registering for events, watching state transitions, and so on.

The IExtensionCollection<T> interface is a collection of the IExtension<T> objects that allow for retrieving the IExtension<T> by its type. IExtensionCollection<T>.Find returns the most recently added object that is an IExtension<T> of that type.

Extensible Objects in Windows Communication Foundation

There are four extensible objects in Windows Communication Foundation (WCF):

  • ServiceHostBase – This is the base class for the service’s host. Extensions of this class can be used to extend the behavior of the ServiceHostBase itself or to store the state for each service.

  • InstanceContext – This class connects an instance of the service’s type with the service runtime. It contains information about the instance as well as a reference to the InstanceContext's containing ServiceHostBase. Extensions of this class can be used to extend the behavior of the InstanceContext or to store the state for each service.

  • OperationContext – This class represents the operation information that the runtime gathers for each operation. This includes information such as the incoming message headers, the incoming message properties, the incoming security identity, and other information. Extensions of this class can either extend the behavior of OperationContext or store the state for each operation.

  • IContextChannel – This interface allows for the inspection of each state for the channels and proxies built by the WCF runtime. Extensions of this class can either extend the behavior of IClientChannel or can use it to store the state for each channel.

The following code example shows the use of a simple extension to track InstanceContext objects.

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();
    }
  }
}

See also