Condividi tramite


Oggetti estensibili

Questo modello viene usato per estendere le classi di runtime esistenti con nuove funzionalità oppure per aggiungere un nuovo stato a un oggetto. Le estensioni, allegate a uno degli oggetti estensibili, attivano i comportamenti in fasi molto diverse dell'elaborazione per accedere a stato e funzionalità condivisi allegati a un oggetto estensibile comune al quale possono accedere.

Criterio IExtensibleObject<T>

Nel modello di oggetti estensibili sono disponibili tre interfacce: IExtensibleObject<T>, IExtension<T> e IExtensionCollection<T>.

L'interfaccia IExtensibleObject<T> viene implementata dai tipi che consentono agli oggetti IExtension<T> di personalizzare la funzionalità.

Gli oggetti estendibili consentono l'aggregazione dinamica di oggetti IExtension<T>. Gli oggetti IExtension<T> sono caratterizzati dall'interfaccia seguente:

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

Tramite la restrizione dei tipi è possibile garantire che le estensioni possano essere definite solo per le classi IExtensibleObject<T>. Attach e Detach forniscono la notifica dell'aggregazione o disaggregazione.

È consigliabile limitare le implementazioni quando possono essere aggiunte e rimosse da un proprietario. Ad esempio, è possibile impedire completamente la rimozione, impedire l'aggiunta o la rimozione delle estensioni quando il proprietario o l'estensione sono in un determinato stato, impedire l'aggiunta contemporaneamente a più proprietari o consentire solo una singola aggiunta seguita da una singola rimozione.

IExtension<T> non implica alcuna interazione con le altre interfacce standard gestite. In particolare, il metodo IDisposable.Dispose sull'oggetto proprietario in genere non disconnette le relative estensioni.

Quando si aggiunge un'estensione alla raccolta, il metodo Attach viene chiamato prima dell'aggiunta alla raccolta. Quando si rimuove un'estensione dalla raccolta, il metodo Detach viene chiamato dopo la rimozione. Ciò significa che (presupponendo che la sincronizzazione avvenga correttamente) un'estensione può essere disponibile nella raccolta solo tra Attach e Detach.

L'oggetto passato al metodo FindAll o al metodo Find non deve necessariamente essere IExtension<T> (ad esempio, è possibile passare qualsiasi oggetto), ma l'estensione restituita è un'interfaccia IExtension<T>.

Se nessuna estensione nella raccolta è IExtension<T>, Find restituisce un valore null e FindAll restituisce una raccolta vuota. Se più estensioni implementano IExtension<T>, Find restituisce una di esse. Il valore restituito dal metodo FindAll è un'istantanea.

Sono disponibili due scenari principali. Nel primo scenario la proprietà Extensions viene usa come un dizionario basato sui tipi per inserire lo stato su un oggetto allo scopo di consentire a un altro componente di ricercarlo usando il tipo.

Nel secondo scenario vengono usate le proprietà Attach e Detach per consentire a un oggetto di partecipare a un comportamento personalizzato, ad esempio la registrazione per gli eventi, il controllo delle transizioni di stato e così via.

L'interfaccia IExtensionCollection<T> è una raccolta degli oggetti IExtension<T> che consentono il recupero di IExtension<T> in base al tipo. IExtensionCollection<T>.Find restituisce l'ultimo oggetto aggiunto che è un oggetto IExtension<T> di quel tipo.

Oggetti estensibili in Windows Communication Foundation

In Windows Communication Foundation (WCF) sono disponibili quattro oggetti estensibili:

  • ServiceHostBase: è la classe base per l'host del servizio. Le estensioni di questa classe possono essere usate per estendere il comportamento di ServiceHostBase o per archiviare lo stato per ogni servizio.

  • InstanceContext: questa classe collega un'istanza del tipo del servizio con il runtime del servizio. Contiene informazioni sull'istanza e un riferimento alla classe InstanceContext che contiene la classe ServiceHostBase. Le estensioni di questa classe possono essere usate per estendere il comportamento di InstanceContext o per archiviare lo stato per ogni servizio.

  • OperationContext: questa classe rappresenta le informazioni dell'operazione che il runtime raccoglie per ogni operazione. Tra queste informazioni sono incluse le intestazioni dei messaggi in arrivo, le proprietà dei messaggi in arrivo, l'identità di sicurezza dei messaggi in arrivo e altre informazioni. Le estensioni di questa classe possono estendere il comportamento di OperationContext o archiviare lo stato per ogni operazione.

  • IContextChannel – Questa interfaccia consente l'ispezione di ogni stato per i canali e i proxy compilati dal runtime di WCF. Le estensioni di questa classe possono estendere il comportamento di IClientChannel o usarlo per archiviare lo stato per ogni canale.

Nell'esempio di codice seguente viene illustrato l'uso di un'estensione semplice per tenere traccia degli oggetti 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();
    }
  }
}

Vedi anche