Configurazione ed estensione del runtime con i comportamenti
I comportamenti consentono di modificare il comportamento predefinito e di aggiungere estensioni personalizzate che controllano e convalidano la configurazione del servizio o modificano il comportamento nel runtime nelle applicazioni client e di servizi Windows Communication Foundation (WCF). In questo argomento vengono descritte le interfacce di comportamento e viene illustrato come implementarle e aggiungerle alla descrizione del servizio (in un'applicazione di servizio) o all'endpoint (in un'applicazione client) a livello di codice o in un file di configurazione. Per altre informazioni sull'uso dei comportamenti forniti dal sistema, vedere Specifica del comportamento di runtime del servizio e Specifica del comportamento di runtime del client.
Comportamenti
I tipi di comportamento vengono aggiunti agli oggetti di descrizione del servizio o dell'endpoint del servizio (rispettivamente nel servizio o nel client) prima che tali oggetti vengano usati da Windows Communication Foundation (WCF) per creare un runtime che esegue un servizio WCF o un client WCF. Quando questi comportamenti vengono chiamati durante il processo di costruzione del runtime, saranno quindi in grado di accedere alle proprietà e ai metodi runtime che modificano il runtime costruito dal contratto, dalle associazioni e dagli indirizzi.
Metodi di comportamento
Tutti i comportamenti dispongono di un metodo AddBindingParameters
, un metodo ApplyDispatchBehavior
, un metodo Validate
e un metodo ApplyClientBehavior
con un'eccezione: poiché l'interfaccia IServiceBehavior non può essere eseguita in un client, non implementa ApplyClientBehavior
.
Utilizzare il metodo
AddBindingParameters
per modificare o aggiungere oggetti personalizzati in una raccolta a cui le associazioni personalizzate potranno accedere per utilizzarli quando viene costruito il runtime. Ad esempio, è in questo modo che vengono specificati i requisiti di protezione che influiscono sulla modalità di costruzione del canale, ma che non sono noti allo sviluppatore del canale.Utilizzare il metodo
Validate
per esaminare la struttura di descrizione e l'oggetto runtime corrispondente al fine di garantirne la conformità a un set di criteri.Utilizzare i metodi
ApplyDispatchBehavior
eApplyClientBehavior
per esaminare la struttura di descrizione e modificare il runtime per un particolare ambito sul servizio o sul client. È inoltre possibile inserire oggetti di estensione.Nota
Anche se in questi tre metodi viene fornita una struttura di descrizione, essa è utilizzabile solo per attività di esame. Se un albero di descrizione viene modificato, il comportamento è indefinito.
L'accesso alle proprietà che possono modificate e alle interfacce di personalizzazione che è possibile implementare avviene attraverso le classi runtime del servizio e del client. I tipi di servizio sono rappresentati dalle classi DispatchRuntime e DispatchOperation. I tipi di client sono rappresentati dalle classi ClientRuntime e ClientOperation. Le classi ClientRuntime e DispatchRuntime sono i punti di ingresso dell'estendibilità per accedere rispettivamente alle proprietà runtime e alle raccolte di estensioni a livello di client e del servizio. Analogamente, le classi ClientOperation e DispatchOperation espongono rispettivamente proprietà runtime e raccolte di estensioni dell'operazione del client e del servizio. È tuttavia possibile accedere all'oggetto runtime di ambito più ampio dall'oggetto runtime dell'operazione e viceversa se necessario.
Nota
Per una discussione sulle proprietà runtime e sui tipi di estensioni che è possibile utilizzare per modificare il comportamento di esecuzione di un client, vedere Estensione dei client. Per una discussione sulle proprietà runtime e sui tipi di estensioni che è possibile utilizzare per modificare il comportamento di esecuzione di un dispatcher di un servizio, vedere Estensione di Dispatcher.
La maggior parte degli utenti WCF non interagisce direttamente con il runtime; ma utilizza costrutti del modello di programmazione principale, ad esempio endpoint, contratti, associazioni, indirizzi e attributi di comportamento su classi o comportamenti nei file di configurazione. Tali costrutti costituiscono l'albero di descrizione, ovvero la specifica completa per la costruzione di un runtime per supportare un servizio o un client descritto da tale struttura.
In WCF esistono quattro tipi di comportamenti:
I comportamenti del servizio (tipi IServiceBehavior) consentono la personalizzazione del runtime dell'intero servizio, incluso ServiceHostBase.
I comportamenti dell'endpoint (tipi IEndpointBehavior) consentono la personalizzazione degli endpoint del servizio e dei relativi oggetti EndpointDispatcher associati.
I comportamenti del contratto (tipi IContractBehavior) consentono la personalizzazione di entrambe le classi ClientRuntime e DispatchRuntime rispettivamente nelle applicazioni client e di servizi.
I comportamenti dell'operazione (tipi IOperationBehavior) consentono la personalizzazione delle classi ClientOperation e DispatchOperation nel client e nel servizio.
È possibile aggiungere questi comportamenti ai vari oggetti di descrizione implementando attributi personalizzati, utilizzando file di configurazione dell'applicazione, oppure è possibile aggiungerli direttamente alla raccolta di comportamenti sull'oggetto di descrizione appropriato. Essi devono tuttavia essere aggiunti a un oggetto di descrizione del servizio o dell'endpoint del servizio prima della chiamata a ICommunicationObject.Open su ServiceHost o ChannelFactory<TChannel>.
Ambiti dei comportamenti
Sono disponibili quattro tipi di comportamento, ognuno dei quali corrisponde a un particolare ambito di accesso runtime.
Comportamenti del servizio
I comportamenti del servizio, che implementano l'interfaccia IServiceBehavior, costituiscono il meccanismo principale attraverso cui è possibile modificare il runtime dell'intero servizio. Esistono tre meccanismi per aggiungere tali comportamenti a un servizio.
Utilizzo di un attributo sulla classe del servizio. Quando viene costruito un oggetto ServiceHost, l'implementazione di ServiceHost utilizza la riflessione per individuare il set di attributi sul tipo del servizio. Se questi attributi sono implementazioni di IServiceBehavior, essi vengono aggiunti alla raccolta di comportamenti su ServiceDescription. In questo modo tali comportamenti possono partecipare alla costruzione del runtime del servizio.
Aggiunta del comportamento alla raccolta di comportamenti su ServiceDescription a livello di codice. Per questa operazione utilizzare le righe di codice seguenti:
ServiceHost host = new ServiceHost(/* Parameters */); host.Description.Behaviors.Add(/* Service Behavior */);
Implementazione di una classe BehaviorExtensionElement personalizzata che estende la configurazione. In questo modo viene consentito l'utilizzo del comportamento del servizio dai file di configurazione dell'applicazione.
Esempi di comportamenti del servizio in WCF includono l'attributo ServiceBehaviorAttribute, ServiceThrottlingBehavior e il comportamento ServiceMetadataBehavior.
Comportamenti del contratto
I comportamenti del contratto, che implementano l'interfaccia IContractBehavior, vengono utilizzati per estendere il runtime del client e del servizio in un contratto.
Esistono due meccanismi per aggiungere tali comportamenti a un contratto. Il primo consiste nella creazione di un attributo personalizzato da utilizzare sull'interfaccia del contratto. Quando un'interfaccia del contratto viene passata a ServiceHost o ChannelFactory<TChannel>, WCF esamina gli attributi sull'interfaccia. Se gli attributi sono implementazioni di IContractBehavior, essi vengono aggiunti alla raccolta di comportamenti sulla classe System.ServiceModel.Description.ContractDescription creata per tale interfaccia.
È inoltre possibile implementare l'interfaccia System.ServiceModel.Description.IContractBehaviorAttribute sull'attributo del comportamento del contratto personalizzato. In questo caso, il comportamento è il seguente quando applicato a:
•Un'interfaccia del contratto. In questo caso, il comportamento viene applicato a tutti i contratti di tale tipo in qualsiasi endpoint e in WCF il valore della proprietà IContractBehaviorAttribute.TargetContract viene ignorato.
•Una classe del servizio. In questo caso, il comportamento viene applicato solo agli endpoint il cui contratto corrisponde al valore della proprietà TargetContract.
•Una classe di callback. In questo caso, il comportamento viene applicato all'endpoint del client duplex e WCF ignora il valore della proprietà TargetContract.
Il secondo meccanismo consiste nell'aggiunta del comportamento alla raccolta di comportamenti su una classe ContractDescription.
Esempi di comportamenti del contratto in WCF includono l'attributo System.ServiceModel.DeliveryRequirementsAttribute. Per ulteriori informazioni e per un esempio, vedere l'argomento di riferimento.
Comportamenti dell'endpoint
I comportamenti dell'endpoint, che implementano l'interfaccia IEndpointBehavior, costituiscono il meccanismo principale attraverso cui è possibile modificare il runtime dell'intero servizio o del client per un endpoint specifico.
Esistono due meccanismi per aggiungere tali comportamenti a un servizio.
Aggiunta del comportamento alla proprietà Behaviors.
Implementazione di una classe BehaviorExtensionElement personalizzata che estende la configurazione.
Per ulteriori informazioni e per un esempio, vedere l'argomento di riferimento.
Comportamenti dell'operazione
I comportamenti dell'operazione, che implementano l'interfaccia IOperationBehavior, vengono utilizzati per estendere il runtime del client e del servizio per ogni operazione.
Esistono due meccanismi per aggiungere tali comportamenti a un'operazione. Il primo meccanismo consiste nella creazione di un attributo personalizzato da utilizzare sul metodo che modella l'operazione. Quando un'operazione viene aggiunta a ServiceHost o ChannelFactory, WCF aggiunge gli attributi di IOperationBehavior alla raccolta di comportamenti sulla classe OperationDescription creata per l'operazione.
Il secondo meccanismo consiste nell'aggiunta diretta del comportamento alla raccolta di comportamenti su una classe OperationDescription costruita.
Esempi di comportamenti dell'operazione in WCF includono OperationBehaviorAttribute e TransactionFlowAttribute.
Per ulteriori informazioni e per un esempio, vedere l'argomento di riferimento.
Utilizzo della configurazione per creare comportamenti
I comportamenti del servizio, dell'endpoint e del contratto possono essere progettati in modo da essere specificati nel codice o mediante attributi; ma solo i comportamenti del servizio e dell'endpoint possono essere configurati utilizzando file di configurazione dell'applicazione o Web. L'esposizione dei comportamenti mediante attributi consente agli sviluppatori di specificare un comportamento in fase di compilazione che non può essere aggiunto, rimosso o modificato a runtime. Tale sistema è spesso adatto per comportamenti che sono sempre necessari per il corretto funzionamento del servizio, ad esempio i parametri correlati alla transazione passati all'attributo System.ServiceModel.ServiceBehaviorAttribute). L'esposizione dei comportamenti mediante la configurazione consente agli sviluppatori di lasciare la specifica e la configurazione di tali comportamenti a coloro che distribuiscono il servizio. Tale sistema è adatto per comportamenti che rappresentano componenti facoltativi o altra configurazione specifica della distribuzione, ad esempio se i metadati vengono esposti per il servizio o la particolare configurazione dell'autorizzazione per un servizio.
Nota
È inoltre possibile utilizzare i comportamenti che supportano la configurazione per imporre criteri dell'applicazione aziendale inserendoli nel file di configurazione machine.config e bloccando tali elementi. Per una descrizione e un esempio, vedere Procedura: Bloccare gli endpoint nell'organizzazione.
Per esporre un comportamento mediante la configurazione, un sviluppatore deve creare una classe derivata di BehaviorExtensionElement e quindi registrare tale estensione nella configurazione.
Nell'esempio di codice seguente viene illustrato come un'interfaccia IEndpointBehavior implementa BehaviorExtensionElement:
// BehaviorExtensionElement members
public override Type BehaviorType
{
get { return typeof(EndpointBehaviorMessageInspector); }
}
protected override object CreateBehavior()
{
return new EndpointBehaviorMessageInspector();
}
Affinché il sistema di configurazione carichi un elemento BehaviorExtensionElement personalizzato, questo deve essere registrato come estensione. Nell'esempio di codice seguente viene illustrato il file di configurazione per il comportamento dell'endpoint precedente.
<configuration>
<system.serviceModel>
<services>
<service
name="Microsoft.WCF.Documentation.SampleService"
behaviorConfiguration="metadataSupport"
>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/ServiceMetadata" />
</baseAddresses>
</host>
<endpoint
address="/SampleService"
binding="wsHttpBinding"
behaviorConfiguration="withMessageInspector"
contract="Microsoft.WCF.Documentation.ISampleService"
/>
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"
/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="metadataSupport">
<serviceMetadata httpGetEnabled="true" httpGetUrl=""/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="withMessageInspector">
<endpointMessageInspector />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add
name="endpointMessageInspector"
type="Microsoft.WCF.Documentation.EndpointBehaviorMessageInspector, HostApplication, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
/>
</behaviorExtensions>
</extensions>
</system.serviceModel>
</configuration>
In tale codice, Microsoft.WCF.Documentation.EndpointBehaviorMessageInspector
è il tipo dell'estensione di comportamento e HostApplication
è il nome dell'assembly in cui la classe è stata compilata.
Ordine di valutazione
System.ServiceModel.ChannelFactory<TChannel> e System.ServiceModel.ServiceHost sono responsabili della compilazione del runtime in base al modello di programmazione e alla descrizione. I comportamenti, come descritto in precedenza, contribuiscono a tale processo di compilazione a livello del servizio, dell'endpoint, del contratto e dell'operazione.
ServiceHost applica i comportamenti nell'ordine seguente:
Servizio
Contract
Endpoint
Operazione
All'interno di una raccolta di comportamenti non è garantito alcun ordine.
ChannelFactory<TChannel> applica i comportamenti nell'ordine seguente:
Contract
Endpoint
Operazione
Anche in questo caso, all'interno di una raccolta di comportamenti non è garantito alcun ordine.
Aggiunta di comportamenti a livello di codice
Le proprietà di System.ServiceModel.Description.ServiceDescription nell'applicazione del servizio non devono essere modificate successivamente al metodo CommunicationObject.OnOpening su System.ServiceModel.ServiceHostBase. Alcuni membri, come la proprietà ServiceHostBase.Credentials e i metodi AddServiceEndpoint
su ServiceHostBase e System.ServiceModel.ServiceHost, generano un'eccezione se vengono modificati dopo questo punto. Altri consentono la modifica, ma il risultato è indefinito.
Analogamente, sul client i valori System.ServiceModel.Description.ServiceEndpoint non devono essere modificati dopo la chiamata a OnOpening su System.ServiceModel.ChannelFactory. La proprietà ChannelFactory.Credentials genera un'eccezione se viene modificata dopo questo punto, ma è possibile modificare senza errore gli altri valori della descrizione client. Il risultato è tuttavia indefinito.
Per il servizio o per il client, è consigliabile modificare la descrizione prima di chiamare CommunicationObject.Open.
Regole di ereditarietà per gli attributi di comportamento
Tutti i quattro tipi di comportamento possono essere popolati utilizzando attributi, ovvero i comportamenti del servizio e i comportamenti del contratto. Poiché gli attributi vengono definiti su oggetti gestiti e membri e gli oggetti gestiti e i membri supportano l'ereditarietà, è necessario definire il funzionamento degli attributi di comportamento nel contesto di ereditarietà.
A livello generale, la regola prevede che per un particolare ambito (ad esempio un servizio, un contratto o un'operazione) vengono applicati tutti gli attributi di comportamento nella gerarchia di ereditarietà dell'ambito. Se sono presenti due attributi di comportamento dello stesso tipo, viene utilizzato solo il tipo più derivato.
Comportamenti del servizio
Per una determinata classe del servizio, vengono applicati tutti gli attributi di comportamento del servizio sulla classe e sugli elementi padre della classe. Se lo stesso tipo di attributo viene applicato in più posizioni nella gerarchia di ereditarietà, viene utilizzato il tipo più derivato.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
[AspNetCompatibilityRequirementsAttribute(
AspNetCompatibilityRequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class A { /* … */ }
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class B : A { /* … */}
Ad esempio, nel caso precedente, il servizio B finisce con una modalità InstanceContextMode paria Single, una modalità AspNetCompatibilityRequirementsMode pari a Allowed e una modalità ConcurrencyMode pari a Single. ConcurrencyMode è Single, poiché l'attributo ServiceBehaviorAttribute sul servizio B è "più derivato" di quello sul servizio A.
Comportamenti del contratto
Per un determinato contratto, vengono applicati tutti gli attributi di comportamento del contratto sull'interfaccia e sugli elementi padre dell'interfaccia. Se lo stesso tipo di attributo viene applicato in più posizioni nella gerarchia di ereditarietà, viene utilizzato il tipo più derivato.
Comportamenti dell'operazione
Se una determinata operazione non esegue l'override di un'operazione astratta o virtuale esistente, non viene applicata alcuna regola di ereditarietà.
Se un'operazione esegue l'override di un'operazione esistente, vengono applicati tutti gli attributi di comportamento dell'operazione sull'operazione e sugli elementi padre dell'operazione. Se lo stesso tipo di attributo viene applicato in più posizioni nella gerarchia di ereditarietà, viene utilizzato il tipo più derivato.