Erweiterbare Objekte

Das erweiterbare Objektmuster wird verwendet, um entweder vorhandene Laufzeitklassen um neue Funktionen zu erweitern oder um einem Objekt neue Zustandsfunktionen hinzuzufügen. Erweiterungen, die einem der erweiterbaren Objekte zugeordnet sind, ermöglichen es Verhalten in verschiedenen Phasen der Verarbeitung, auf gemeinsam verwendete Zustände und Funktionen zuzugreifen, die an ein zugängliches und allgemeines erweiterbares Objekt angefügt sind.

Das IExtensibleObject<T-Muster>

Es gibt drei Schnittstellen im erweiterbaren Objektmuster: IExtensibleObject<T>, IExtension<T> und IExtensionCollection<T>.

Die IExtensibleObject<T>-Schnittstelle wird von Typen implementiert, die es IExtension<T>-Objekten ermöglichen, ihre Funktionalität anzupassen.

Erweiterbare Objekte ermöglichen die dynamische Aggregation von IExtension<T>-Objekten. IExtension<T>-Objekte sind durch die folgende Schnittstelle gekennzeichnet:

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

Die Typbeschränkung stellt sicher, dass Erweiterungen nur für Klassen definiert werden können, für die IExtensibleObject<T> gilt. Attach und Detach führen die Benachrichtigung über eine Aggregation oder Disaggregation durch.

Bei Implementierungen können Beschränkungen verwendet werden, wenn sie einem Besitzer hinzugefügt bzw. wenn sie entfernt werden. Sie können zum Beispiel die Entfernung vollständig unmöglich machen, indem Sie das Hinzufügen oder Entfernen von Erweiterungen untersagen, wenn sich der Besitzer oder die Erweiterung in einem bestimmten Zustand befinden, das gleichzeitige Hinzufügen zu mehreren Besitzern untersagen oder nur eine einzelne Hinzufügung gefolgt von einer einzelnen Entfernung zulassen.

IExtension<T> impliziert keine Interaktionen mit anderen standardmäßigen verwalteten Schnittstellen. Die IDisposable.Dispose-Methode des Besitzerobjekts trennt ihre Erweiterungen in der Regel nicht ab.

Wenn der Auflistung eine Erweiterung hinzugefügt wird, wird Attach aufgerufen, bevor die Erweiterung in die Auflistung aufgenommen wird. Wenn eine Erweiterung aus der Auflistung entfernt wird, wird nach der Entfernung Detach aufgerufen. Dies bedeutet (vorausgesetzt, es erfolgt eine entsprechende Synchronisierung), dass eine Erweiterung nur dann sicher in einer Auflistung enthalten ist, wenn sie sich zwischen den Zuständen Attach und Detach befindet.

Für das Objekt, das an FindAll oder Find übergeben wird, muss nicht IExtension<T> gelten (beispielsweise können Sie jedes Objekt übergeben), aber bei der zurückgegebenen Erweiterung handelt es sich um IExtension<T>.

Wenn keine Erweiterung in der Auflistung eine IExtension<T> ist, gibt Find NULL und FindAll eine leere Auflistung zurück. Wenn mehrere Erweiterungen IExtension<T> implementieren, gibt Find eine davon zurück. Der von FindAll zurückgegebene Wert ist eine Momentaufnahme.

Es gibt zwei Hauptszenarios. Beim ersten Szenario wird die Extensions-Eigenschaft als typbasiertes Wörterbuch verwendet, um den Zustand für ein Objekt einzufügen und auf diese Weise eine andere Komponente in die Lage zu versetzen, anhand des Typs danach zu suchen.

Beim zweiten Szenario werden die Eigenschaften Attach und Detach verwendet, um einem Objekt die Teilnahme am benutzerdefinierten Verhalten zu ermöglichen, zum Beispiel das Registrieren von Ereignissen, Beobachten von Zustandsübergängen usw.

Die IExtensionCollection<T>-Schnittstelle ist eine Auflistung von IExtension<T>-Objekten, für die der Abruf von IExtension<T> anhand des Typs zulässig ist. IExtensionCollection<T>.Find gibt das zuletzt hinzugefügte Objekt zurück, das eine IExtension<T> dieses Typs ist.

Erweiterbare Objekte in Windows Communication Foundation

Es gibt vier erweiterbare Objekte in Windows Communication Foundation (WCF):

  • ServiceHostBase – Dies ist die Basisklasse für den Host des Dienstes. Sie können die Erweiterungen dieser Klasse verwenden, um das Verhalten von ServiceHostBase selbst zu erweitern oder um den Zustand für die Dienste einzeln zu speichern.

  • InstanceContext – Diese Klasse verbindet eine Instanz des Diensttyps mit der Dienstlaufzeit. Sie enthält Informationen zu der Instanz sowie einen Verweis auf den InstanceContext, der ServiceHostBase enthält. Sie können die Erweiterungen dieser Klasse verwenden, um das Verhalten von InstanceContext zu erweitern oder um den Zustand für die Dienste einzeln zu speichern.

  • OperationContext – Diese Klasse stellt die Vorgangsinformationen dar, die die Laufzeit für jeden Vorgang aufzeichnet. Dazu gehören auch Informationen wie Header von eingehenden Nachrichten, Eigenschaften von eingehenden Nachrichten, die eingehende Sicherheitsidentität und andere Informationen. Erweiterungen dieser Klasse können entweder das Verhalten des OperationContext erweitern oder den Zustand für jeden Vorgang einzeln speichern.

  • IContextChannel– Diese Schnittstelle ermöglicht die Untersuchung der einzelnen Zustände für die Kanäle und Proxys, die von der WCF-Laufzeit erstellt werden. Erweiterungen dieser Klasse können entweder das Verhalten des IClientChannel erweitern oder das Verhalten verwenden, um den Zustand für jeden Kanal einzeln zu speichern.

Das folgende Codebeispiel zeigt die Verwendung einer einfachen Erweiterung zum Verfolgen von InstanceContext-Objekten.

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

Siehe auch