Condividi tramite


Informazioni su Enterprise Services (COM+) in .NET

 

Shannon Pahl
Microsoft Corporation

Aprile 2002

Riepilogo: fornisce dettagli tecnici sull'integrazione dei servizi Microsoft .NET e COM+ e descrive i servizi disponibili per il codice gestito. (26 pagine stampate)

Contenuto

Introduzione
Transazioni
Distribuzione
Componenti serviti
Durate degli oggetti
Sicurezza
Componenti remoti
Conclusione

Introduzione

Questo articolo richiede una certa familiarità con i servizi Microsoft.NET® Framework e COM+. La familiarità con i servizi aziendali non è necessaria, ma può essere utile. Per una buona base su questi argomenti, vedere:

COM consente di scrivere applicazioni basate su componenti. È noto che il lavoro di idraulica necessario per scrivere componenti COM è significativo e ripetitivo. COM+ non è così tanto circa una nuova versione di COM; COM+ fornisce invece un'infrastruttura di servizi per i componenti. I componenti vengono compilati e quindi installati nelle applicazioni COM+ per creare applicazioni server scalabili che raggiungono una velocità effettiva elevata con facilità di distribuzione. Se un componente non deve usare alcun servizio, non deve essere inserito in un'applicazione COM+. La scalabilità e la velocità effettiva si ottengono progettando applicazioni fin dall'inizio per usare servizi come transazioni, pool di oggetti e semantica di attività.

.NET Framework offre un altro modo per scrivere applicazioni basate su componenti e presenta i vantaggi del modello di programmazione COM di un supporto migliore degli strumenti, common language runtime (CLR) e una sintassi di codifica molto più semplice. L'infrastruttura dei servizi COM+ è accessibile da codice gestito e non gestito. I servizi nel codice non gestito sono noti come servizi COM+. In .NET questi servizi sono detti Servizi aziendali. La derivazione di una classe da ServicedComponent indica che i servizi saranno necessari per un componente. Se un componente non deve usare alcun servizio, non deve derivare da ServicedComponent. Il supporto degli strumenti è stato migliorato per consentire ai programmatori di scrivere applicazioni basate su server, ma gli stessi problemi di scalabilità e velocità effettiva sono ancora nell'ambito delle procedure di programmazione consigliate. L'idea di base alla base dei servizi èdsign per la velocità effettiva e la scalabilità fin dall'inizio e sfruttare i servizi aziendali per implementare facilmente questi modelli di progettazione, se appropriato.

Si potrebbe sostenere che la progettazione dell'infrastruttura dei servizi in realtà ha poco a che fare con i componenti COM o anche componenti: i servizi COM+ possono ora essere applicati ai componenti COM, ai componenti .NET e anche ad altre entità che non sono considerate componenti, ad esempio pagine ASP o blocchi di codice arbitrario (vedere la funzionalità Servizi senza componenti COM+ in Microsoft Windows® XP).

Tutti i servizi COM+ attualmente disponibili sono disponibili per gli oggetti .NET e COM. Alcuni di questi servizi includono transazioni, pool di oggetti e stringhe di costruzione, JIT, sincronizzazione, sicurezza basata sui ruoli, CRM e BYOT. Per un elenco completo dei servizi in Microsoft Windows 2000, vedere Servizi forniti da COM+ in Platform SDK. Microsoft Windows XP include una nuova versione di COM+, ovvero COM+ 1.5, che include servizi aggiuntivi che possono essere usati anche con i componenti .NET.

Transazioni

Per scrivere applicazioni gestite che usano servizi, le classi che richiedono servizi devono derivare da ServicedComponent e usare vari attributi personalizzati per specificare i servizi effettivi necessari. Questa sezione presenta questi concetti e come influiscono sulla scrittura di codice gestito. Nelle sezioni successive viene fornita una spiegazione più dettagliata.

Si supponga che sia stata scritta una classe Account (il codice effettivo è elencato più avanti) e che si trovi nell'assembly BankComponent. Questa classe può essere usata come segue:

BankComponent Client

using system;
using BankComponent;
namespace BankComponentClient
{
      class Client
      {
        public static int Main() 
        {
          Account act = new Account();
          act.Post(5, 100);
          act.Dispose();
          return 0;
        }
      }
}

Per compilare il client, è necessario aggiungere il riferimento allo spazio dei nomi BankComponent. Inoltre, è necessario aggiungere un riferimento per l'assembly System.EnterpriseServices, nello spazio dei nomi BankComponentClient, il client chiama Dispose() e il costruttore ServicedComponent, che sono metodi definiti in System.EnterpriseServices, non l'assembly contenente BankComponent. Si tratta di un requisito .NET generale quando una classe derivata non esegue l'override di tutti i metodi della classe base.

Il codice BankComponent Server mostra l'implementazione della classe Account in .NET, che usa le transazioni. La classe Account deriva dalla classe System.EnterpriseServices.ServicedComponent. L'attributo Transaction contrassegna la classe come richiesta di una transazione. I servizi Di sincronizzazione e JIT vengono configurati automaticamente perché viene usato l'attributo Transaction. L'attributo AutoComplete viene usato per specificare che il runtime deve chiamare automaticamente la funzione SetAbort per la transazione se viene generata un'eccezione non gestita durante l'esecuzione del metodo; in caso contrario, viene chiamata una funzione SetComplete . L'attributo ApplicationName associa questo assembly a un'applicazione COM+ che archivia i dati di configurazione del servizio per questa applicazione. Nel codice sono evidenziate ulteriori modifiche necessarie per questa classe.

BankComponent Server

using System.EnterpriseServices;
[assembly: ApplicationName("BankComponent")]
[assembly: AssemblyKeyFileAttribute("Demos.snk")]

namespace BankComponentServer
{
      [Transaction(TransactionOption.Required)]
      public class Account : ServicedComponent
      {
            [AutoComplete]
            public bool Post(int accountNum, double amount)
            {
            // Updates the database, no need to call SetComplete.
            // Calls SetComplete automatically if no exception is thrown.
            }
      }
}

Il codice nello spazio dei nomi BankComponent Server mostra quanto sia facile usare i servizi COM+ in .NET. Di seguito è riportato un riepilogo del processo completo dalla scrittura di codice alla distribuzione:

  1. Scrivere l'assembly del server.

  2. Compilare l'assembly:

    1. Firmare l'assembly. Un file di chiave può essere generato una volta per il progetto. Non è necessario generarne uno per ogni compilazione. È possibile creare una chiave usando il prompt dei comandi di Microsoft .NET e sn.exe:

      sn –k Demos.snk
      
    2. Compilare il codice. È necessario aggiungere un riferimento per System.EnterpriseServices.

  3. Distribuire l'applicazione.

    Un assembly che utilizza componenti serviti deve essere registrato con il catalogo COM+. La classe ServicedComponent e gli attributi personalizzati sono i due concetti chiave per ottenere l'accesso ai servizi COM+ dal codice gestito. La configurazione del servizio viene archiviata nel catalogo COM+. Gli oggetti risiedono ed eseguono all'interno di CLR. L'oggetto gestito e il contesto COM+ associato sono illustrati nella figura 1 e diventeranno più chiari nelle due sezioni successive.

    Figura 1. Servizi associati ai componenti gestiti

    Con i componenti COM+ è necessario configurare manualmente il catalogo, ma con i componenti gestiti, il catalogo può essere aggiornato in base agli attributi nel codice. Un assembly può essere registrato in modo esplicito usando lo strumento da riga di comando regsvcs.exe o scrivendo script che accedono a un'API gestita. Altri dettagli sono disponibili nella sezione dei dettagli della distribuzione di seguito. Durante lo sviluppo, la distribuzione di XCopy viene fornita per praticità semplicemente copiando l'assembly nella directory dell'applicazione. Ogni volta che un'applicazione client crea istanze di classi derivate da ServicedComponent, il runtime rileva se l'assembly è già stato registrato in un'applicazione COM+. Se non è stato registrato, la directory locale viene cercata l'assembly e, se presente, tutti i componenti serviti nell'assembly vengono registrati in un'applicazione COM+ e l'attivazione può procedere. Questa operazione è nota come registrazione differita, ma non funziona per tutti gli scenari. Ad esempio, gli assembly contrassegnati come app server COM+ richiedono la registrazione esplicita (vedere di seguito) e la registrazione differita non funziona per i client non gestiti che chiamano componenti gestiti gestiti. La registrazione differita è utile durante il tempo di sviluppo. In caso contrario, usare script, codice o RegSvcs per registrare l'assembly.

  4. Inserire l'assembly nella GAC. Per altri dettagli, vedere la sezione relativa alla distribuzione.

  5. Eseguire il client.

Distribuzione

Gli attributi personalizzati sono uno dei due concetti chiave per l'accesso ai servizi COM+ dal codice gestito. Gli attributi personalizzati vengono usati per specificare i servizi necessari, ad esempio l'attributo personalizzato Transaction nell'elenco di codice precedente. Questi attributi archiviano le opzioni di configurazione per un servizio nei metadati dell'assembly. Gli attributi personalizzati vengono usati con il caricamento di codice dell'assembly e l'uso della reflection, creare istanze degli attributi e chiamare i metodi sull'attributo per estrarre la configurazione del servizio archiviata nell'attributo . Le informazioni possono quindi essere scritte nel catalogo COM+. Il codice che esegue questi e altri passaggi è contenuto in EnterpriseServices.RegistrationHelper. Per semplificare il processo di registrazione, tutti i moduli di registrazione usano il componente EnterpriseServices.RegistrationHelper. Questo componente è accessibile come classe gestita e come oggetto COM.

Figura 2. Registrazione dei componenti serviti

Concettualmente, RegistrationHelper esegue i passaggi seguenti:

  • Usa RegistrationServices.RegisterAssembly per registrare l'assembly nel Registro di sistema. Pertanto, le classi vengono visualizzate nel Registro di sistema come componenti COM scritti nel codice gestito e hanno la chiave InprocServer32 che punta a mscoree.dll. Se una classe gestita non implementa interfacce, i metodi pubblici della classe non vengono visualizzati nel catalogo COM+, a meno che non venga usato ClassInterfaceAttribute. Ciò significa che la configurazione del servizio associata al livello di metodo non può essere archiviata nel catalogo. Tuttavia, alcuni servizi COM+ possono essere configurati a livello di metodo e richiedono al componente di esporre un'interfaccia come visualizzato nel catalogo COM+. Ad esempio, la sicurezza basata sul ruolo COM+ a livello di metodo richiede un componente per implementare un'interfaccia per configurare il servizio. Altre informazioni sono illustrate su questo problema nella sezione sicurezza.
  • Genera una libreria di tipi COM dall'assembly usando TypeLibConverter. ConvertAssemblyToTypeLib.
  • Registra la libreria dei tipi. Finora, questo è molto uguale a RegAsm.exe /tlb.
  • Trova o crea un'applicazione COM+. Il nome viene estratto dall'attributo ApplicationName, dal nome dell'assembly o dal nome dell'applicazione specificato/GUID.
  • Usa la libreria dei tipi per configurare l'applicazione COM+ usando le API di amministrazione COM+.
  • Passa attraverso tutti gli attributi personalizzati e usa IConfigurationAttribute per scrivere i dati di configurazione per il servizio specifico nel catalogo COM+.

RegistrationHelper tenterà di eseguire questi passaggi all'interno di una transazione usando RegistrationHelperTx, una classe all'interno di un'applicazione COM+ creata quando è installato .NET. Pertanto, se la registrazione ha esito negativo, il catalogo COM+ e il Registro di sistema verranno ripristinati allo stato originale. Attualmente, le librerie dei tipi generate rimarranno tuttavia su disco (o nella gaC se l'assembly era nella gaC). Se l'assembly registrato fa riferimento ad altri assembly che usano anche i servizi COM+, tutti gli assembly nel grafico delle dipendenze subiranno gli stessi passaggi elencati in precedenza.

Poiché RegistrationHelper accede al catalogo COM+, richiede autorizzazioni di codice non gestite e diritti di amministratore nel computer. Pertanto, lo stesso vale per i client a RegistrationHelper in nome: registrazione lazy, RegSvcs o script/codice. Ciò implica anche che il codice scaricato da Internet o archiviato in una condivisione di rete non può essere registrato.

È possibile codificare combinazioni di attributi incompatibili, ad esempio richiedendo una transazione e impostando la sincronizzazione su disabilitata. Queste combinazioni vengono attualmente rilevate durante l'ora di registrazione durante la scrittura nel catalogo COM+ e non durante il periodo di compilazione. Alcuni attributi hanno dipendenze implicite su altri attributi, ad esempio quando si usa solo l'attributo Transaction, questo equivale all'uso degli attributi Transaction, JustInTimeActivation e Sync. Quando viene registrato un componente gestito, i valori predefiniti del catalogo COM+ vengono usati a meno che gli attributi non vengano usati per sovrascrivere i valori predefiniti "non configurati". Ad esempio, se un componente è registrato e non ha un attributo Transaction specificato, il valore predefinito non configurato per l'impostazione di transazione nel catalogo è impostato su TransactionOption.Disabled. Questo approccio consente a uno sviluppatore di rimuovere un attributo dal codice se il componente non lo richiede più e quindi quando l'assembly viene registrato di nuovo, la voce del catalogo per la transazione viene reimpostata in modo appropriato. Un elenco dettagliato di questi valori predefiniti non configurati viene specificato nella documentazione online. I valori configurati predefiniti sono i valori predefiniti nei parametri di un attributo, ad esempio usando l'attributo [Transaction] indica TransactionOption.Required.

Poiché i dati di configurazione per i servizi nelle classi gestite vengono archiviati nel catalogo COM+, alcune voci del catalogo possono essere modificate anche in modo amministrativo dopo la registrazione di un assembly. Alcuni servizi non devono essere modificati in questo modo. Ad esempio, la disabilitazione del servizio transazioni nel catalogo può causare il funzionamento errato del codice. Le impostazioni specifiche della distribuzione, ad esempio stringhe di costruzione di oggetti e ruoli di sicurezza, possono essere modificate dopo la registrazione. La distribuzione XCopy di assembly contenenti componenti di servizio potrebbe non essere sufficiente quando vengono eseguite le impostazioni di registrazione successive. La funzionalità di importazione ed esportazione dell'applicazione COM+ consente di distribuire lo stato corrente dell'applicazione. Altre informazioni sull'importazione ed esportazione vengono fornite nella sezione comunicazione remota.

In alcuni casi il catalogo non viene consultato per i dati di configurazione, ma viene estratto esclusivamente dai metadati dell'assembly. I casi sono per Il completamento automatico, JIT, Pool di oggetti (anche se le dimensioni del pool vengono estratte dal catalogo) e l'attributo del metodo sicuro. Altre informazioni su questo problema vengono illustrate nelle sezioni che descrivono tali servizi.

Il processo di registrazione di un assembly genererà automaticamente i GUID richiesti da COM+. Se l'assembly non è firmato, i GUID vengono generati in base solo ai nomi di tipo e spazio dei nomi. Pertanto, se l'assembly non è firmato, è possibile generare GUID non univoci. Un destino simile viene imposto agli assembly .NET che non usano nemmeno i servizi COM+ ma richiedono nomi di tipo univoci. Pertanto, ènecessario firmare un ssemblies che usa i servizi COM+. La registrazione avrà esito negativo se gli assembly non sono firmati. La registrazione implica anche che una classe .NET che usa i servizi COM+ dispone di un archivio dati di configurazione globale. Anche se è possibile copiare assembly privati in più directory dell'applicazione, tutte queste applicazioni fanno infine riferimento a uno dei dati di configurazione per un componente di servizio. Pertanto, la modifica dei dati di configurazione nel catalogo COM+ influisce su tutte le applicazioni che usano la classe. Questo è evidente con più vroot in una configurazione di Microsoft ASP.NET che include tutte una copia dello stesso assembly che usa componenti di servizio. Un modo per avere più configurazioni per la stessa applicazione COM+ consiste nell'usare partizioni COM+ in Microsoft Windows .NET. Per usare il servizio partizioni COM+ in .NET, non usare l'attributo ApplicationID, per installare lo stesso componente in più partizioni, COM+ richiede ID applicazione univoci.

In generale, la gaC viene usata ogni volta che un client deve accedere agli assembly che non si trovano nella stessa directory della directory dell'applicazione client o se l'assembly viene caricato in un altro processo, che non si trova nella directory del client. Concettualmente, gli assembly privati che usano i componenti di servizio sono in realtà assembly condivisi, ovvero l'uso dei dati di configurazione condivisi. Se ApplicationActivationOption è impostato sulla libreria, è possibile usare le transazioni in una classe in un assembly e usare tale assembly in un client se tutti gli assembly vengono caricati dalla stessa directory. Quando un assembly che usa ApplicationActivationOption è impostato sul server, l'assembly viene caricato da dllhost.exe, che probabilmente non si trova nella stessa directory del client. Gli assembly che usano componenti serviced nelle app server COM+ devono essere inseriti nella gaC. Gli assembly che usano componenti serviced nelle librerie COM+ potrebbero non essere inseriti nella gaC (a meno che non si trovino in directory diverse). L'unica eccezione è quando si ospita con ASP.NET- Gli assembly non devono essere inseriti nella gaC per abilitare la copia shadow per funzionare correttamente.

Per rimuovere un'applicazione .NET che usa componenti di servizio, rimuovere l'assembly dalla GAC (se è stato registrato con gaC), registrare l'assembly da COM+ usando regsvcs.exe quindi eliminare l'assembly e le librerie di tipi associate.

Controllo delle versioni

È possibile correggere i GUID richiesti da COM+ usando un attributo GUID. Tuttavia, è consigliabile usare il controllo delle versioni anziché usare in modo esplicito GUID. Ogni volta che viene creata una nuova firma del metodo o quando le classi vengono decorate con attributi di servizio diversi, il numero di versione principale o secondario dell'assembly deve essere incrementato. La registrazione deve essere eseguita una sola volta per una determinata versione. Quando si registra una nuova versione dell'assembly, vengono generati nuovi GUID per tale versione e i componenti vengono registrati nella stessa applicazione COM+ usando lo stesso nome del componente. I componenti verranno quindi visualizzati più volte nell'applicazione COM+. Tuttavia, ogni componente ha ID univoci specificati dai GUID. Ogni istanza fa riferimento a una determinata versione del componente. Si noti spesso quando si creano applicazioni .NET tramite Microsoft Visual Studio® .NET. L'ambiente aggiunge l'attributo [assembly: AssemblyVersion("1.0.*")] a un progetto. Ogni nuova compilazione del progetto genererà un nuovo numero di compilazione e quindi vengono generati nuovi GUID quando l'assembly viene nuovamente registrato. Pertanto, può essere preferibile aumentare manualmente i numeri di compilazione quando appropriato. I client verranno associati a un assembly usando criteri di versione CLR e quindi verrà usata la versione corretta della classe nell'applicazione COM+. Alcuni scenari side-by-side durante la scrittura di assembly (server gestiti) che usano componenti di servizio sono: (alcuni aspetti dell'attivazione usati di seguito verranno descritti nella sezione successiva)

  • Client gestito, server gestito, nessun GUID fisso usato nell'assembly.
  • Il client caricherà l'assembly specificato dai criteri di versione.
  • Client gestito, server gestito, GUID fissi usati.
  • Se il client attiva una classe e usa i criteri di versione per ottenere una versione precedente dell'assembly, il GUID fisso nel codice verrà usato durante l'attivazione per estrarre le informazioni del servizio dal catalogo. Pertanto, le informazioni dell'ultimo assembly registrato che usano questo GUID verranno usate per creare l'oggetto, che in realtà può essere la versione più recente e quindi esisterebbe un'eccezione cast di tipi quando si tenta di eseguire il cast dall'oggetto effettivamente creato (v2) al riferimento nel codice (v1).
  • Il client gestito, il server gestito, nessun GUID fisso, modifica solo il numero di compilazione.
  • Anche se verranno generati nuovi GUID, la libreria dei tipi avrà comunque lo stesso numero di versione perché le librerie di tipi hanno solo due numeri per la versione. Questa operazione può funzionare ancora, ma se la versione 2 è installata sulla versione 1, la versione 1 viene disinstallata, la libreria dei tipi per la versione 2 verrà annullata. Soluzione 1: la versione successiva di .NET Framework (V1.1) risolve questo problema abilitando la versione della libreria dei tipi in modo indipendente dall'assembly. Ciò implica che quando si modifica il numero di versione dell'assembly, la versione della libreria dei tipi deve essere modificata. Soluzione 2: usare solo i numeri di versione principali e secondari.
  • Client non gestito, server gestito, nessun GUID fisso usato.
    • Il client userà un GUID per creare il componente. L'interoperabilità risolve il GUID in un nome e quindi viene applicato il criterio di versione. Se la versione 1 e la versione 2 di un assembly si trovano in un computer e i criteri vengono usati per ottenere la versione 2, il client non gestito otterrà la versione 2.
    • Installare la versione 1, installare la versione 2, disinstallare la versione 1. Ora il client non può creare il componente a meno che non siano presenti criteri di versione da reindirizzare alla versione 2. Inoltre, le voci del Registro di sistema devono esistere per le informazioni di registrazione della versione 1. Un modo per creare informazioni del Registro di sistema per una versione 1 disinstallata consiste nell'usare la funzionalità di aliasing COM+ in Windows XP.

Il controllo delle versioni si applica a tutti i componenti nella stessa applicazione COM+, ovvero non esiste un modo automatico per versione dell'applicazione stessa. Ad esempio, i ruoli dell'applicazione non possono essere versioneti usando i criteri di versione. Usare l'attributo nome applicazione per eseguire il controllo delle versioni dell'applicazione.

Componenti di servizio

Activation

L'infrastruttura di Servizi aziendali si basa sul concetto di contesto. Un contesto è un ambiente per gli oggetti con requisiti di esecuzione simili. I servizi possono essere applicati durante l'attivazione e/o durante l'intercettazione delle chiamate al metodo. Anche se i servizi COM+ sono scritti in codice non gestito, l'integrazione di servizi COM+ con .NET è molto più profonda rispetto all'uso della tecnologia di interoperabilità COM in .NET. Senza derivare da ServicedComponent, il processo di registrazione non avrà l'effetto desiderato.

I componenti serviti possono essere attivati e ospitati in una varietà di combinazioni. Come illustrato nella figura 3, questa discussione farà riferimento a tre casi, in-process (stesso dominio app), tra domini app (stesso processo) e attivazioni tra processi. L'importanza di questi casi è costituita dai limiti che vengono superati durante l'esecuzione di chiamate ai componenti. L'attivazione in-process dà origine a un potenziale limite tra contesti, il caso tra domini app ha sia il contesto incrociato che i limiti tra domini dell'app, mentre il caso tra processi si occupa di cross machine/cross process e cross context boundaries.

Figura 3. Host di attivazione per i componenti serviti

L'implementazione dei componenti serviti si basa sulla comunicazione remota .NET, che fornisce un meccanismo estendibile per collegare i servizi scritti in codice non gestito o gestito. I componenti serviti derivano da ContextBoundObject e implementano varie interfacce, ad esempio IDisposable. La catena di attivazione in CLR viene facilmente personalizzata usando gli attributi personalizzati derivati da ProxyAttribute. L'intercettazione può essere personalizzata scrivendo proxy reali personalizzati. Quando è necessaria una nuova classe derivata del componente gestito, la catena di attivazione viene personalizzata in modo che la chiamata di attivazione chiami effettivamente un wrapper C++ gestito per CoCreateInstance. Ciò consente a COM+ di configurare i contesti e i servizi non gestiti in base alle informazioni archiviate nel catalogo COM+ da un assembly registrato in precedenza. Questa è anche la fase in cui viene implementata la registrazione differita. Durante la registrazione dell'assembly, la chiave InprocServer32 punta a mscoree.dll, reindirizzando quindi COM+ CreateInstance al runtime per creare l'oggetto gestito reale. Pertanto, durante l'attivazione, viene creato un oggetto proxy reale personalizzato. La versione in-process di questo proxy è nota come proxy del componente serviced o SCP. Questa immagine è illustrata nella figura 4.

Figura 4. Percorso di attivazione

Il percorso restituito dalla chiamata di attivazione esegue il marshalling dei riferimenti gestiti dal codice gestito, tramite COM+ non gestito e di nuovo nel codice gestito (il percorso inverso della riga 1 nella figura 4). A seconda della posizione in cui è stato creato l'oggetto reale, il lato client annulla il marshalling del riferimento nel modulo pertinente. Nel caso dell'attivazione in-process, la figura 5 indica che il riferimento non èmarshaled come riferimento diretto al proxy trasparente (TP). I riferimenti tra domini app non sono separati come proxy di comunicazione remota .NET. Per i riferimenti tra processi o tra computer (figura 6) è necessario un'operazione maggiore: l'interoperabilità COM effettua chiamate su IManagedObject implementata da ServicedComponent durante l'attivazione e l'annullamento delmarshaling. Il proxy del componente con servizio remoto (RSCP) effettua chiamate su IServicedComponentInfo durante l'attivazione per ottenere l'URI dell'oggetto server, il che implica che vengono effettuate due chiamate remote durante l'attivazione. Quando è necessaria la sicurezza basata sui ruoli COM+ a livello di metodo, queste interfacce devono essere associate a un ruolo affinché l'annullamento delmarshaling abbia esito positivo quando l'infrastruttura effettua chiamate a queste interfacce. La sezione relativa alla sicurezza illustra le implicazioni che l'attivazione tra processi e il marshalling hanno sulla configurazione della sicurezza basata sui ruoli.

Figura 5. Infrastruttura per nelle chiamate di processo

Figura 6. Infrastruttura per chiamate out-of-process

La catena di attivazione è stata quindi personalizzata per creare un proxy reale personalizzato (usato per l'intercettazione) e per creare i contesti non gestiti, lasciando COM+ solo con l'infrastruttura di contesto necessaria per eseguire la semantica dei servizi di intercettazione. Il contesto COM+ è ora associato a un oggetto gestito, non a un oggetto COM.

Interception

La figura 7 illustra l'infrastruttura di chiamata al metodo in-process. Il proxy personalizzato (SCP) consente alle chiamate gestite di essere intercettate. Durante l'attivazione, l'ID di contesto COM+ viene archiviato in SCP. Quando un oggetto gestito chiama un componente gestito, l'ID di contesto archiviato nell'SCP di destinazione viene confrontato con l'ID di contesto del contesto corrente. Se gli ID di contesto sono uguali, la chiamata viene eseguita direttamente sull'oggetto reale. Quando gli ID di contesto sono diversi, SCP effettua una chiamata a COM+ per cambiare i contesti ed eseguire il rendering del servizio di immissione della chiamata al metodo. Per le chiamate in-process, questo è simile a AppDomain.DoCallBack, ma con AppDomain com+. La funzione "DoCallBack" immette prima COM+ (passaggio 2 nella figura 7), che cambia il contesto ed esegue il rendering del servizio, quindi la funzione di callback chiama SCP. SCP esegue il marshalling dei dati e chiama il metodo sull'oggetto reale. Quando il metodo viene chiuso, il percorso restituito consente a COM+ di eseguire il rendering della semantica per uscire da una chiamata al metodo (passaggio 5 nella figura 7) . COM+ viene usato solo per eseguire il rendering del servizio. Il marshalling dei dati e la chiamata al metodo vengono eseguiti all'interno del runtime .NET*,* in modo che la conversione di tipi come String in BSTR non sia necessaria quando si chiamano i metodi. Il marshalling dei dati sarebbe necessario se è stata usata l'interoperabilità COM per le chiamate in-process*.* La chiamata per eseguire il rendering del servizio nel codice non gestito non è pertanto una chiamata di interoperabilità COM per le chiamate in-process.

Figura 7. Infrastruttura per nelle chiamate di processo

Le chiamate ai metodi statici non vengono inoltrate ai proxy trasparenti e reali. Pertanto, i metodi statici non possono utilizzare i servizi di intercettazione; vengono invece chiamati all'interno del contesto del client. I metodi interni verranno chiamati all'interno del contesto corretto, ovvero un client che chiama un metodo interno su un oggetto configurato per una nuova transazione parteciperà a una nuova transazione. Tuttavia, poiché i servizi a livello di metodo richiedono un'interfaccia nel catalogo COM+ (più avanti in questo argomento e nella sicurezza), i metodi interni non possono essere configurati per i servizi a livello di metodo. I servizi possono essere applicati alle proprietà, ma gli attributi a livello di metodo (ad esempio AutoComplete) devono essere inseriti singolarmente nei metodi getter e setter.

L'attributo Completamento automatico è un modo pratico per usare le transazioni senza scrivere codice per accedere al servizio. In alternativa, è possibile usare ContextUtil.SetAbort o ContextUtil.SetComplete. Questo servizio è configurabile in COM+ Explorer impostando una casella di controllo sulle proprietà del metodo. Tuttavia, gli oggetti gestiti non devono implementare interfacce. Questo vale anche per i componenti serviti. Quando il metodo non viene dichiarato in un'interfaccia, la configurazione per i servizi a livello di metodo non può essere scritta nel catalogo alla registrazione; la configurazione può essere archiviata solo nei metadati. Quando non esiste alcuna interfaccia per il metodo, il cambio di contesto viene eseguito da SCP usando le informazioni di configurazione archiviate in IRemoteDispatch.RemoteDispatchAutoDone quando è presente l'attributo AutoComplete. Quando il completamento automatico non è presente, viene usato IRemoteDispatch.RemoteDispatchNotAutoDone. IRemoteDispatch è un'interfaccia implementata da ServicedComponent. I client non gestiti possono chiamare solo componenti serviti che non dispongono di interfacce che usano IDispatch (associazione tardiva) e pertanto la semantica di completamento automatico non può essere applicata a causa dell'assenza di un proxy reale in questo caso. Anche quando vengono usate le interfacce, la configurazione del completamento automatico è ancora basata sui metadati per i client gestiti. La chiamata al metodo DCOM viene eseguita su RemoteDispatchAutoDone solo nel caso out-of-process. I componenti out-of-process non usano il meccanismo DoCallBack, ma DCOM può essere usato per recapitare la chiamata ed eseguire il rendering del servizio. Se il metodo si trova in un'interfaccia, il metodo di interfaccia nel componente collegato remoto viene chiamato tramite DCOM, altrimenti la chiamata viene inviata all'interfaccia IRemoteDispatch in ServicedComponent. Ciò significa che anche le chiamate come Dispose() vengono chiamate tramite DCOM, le cui implicazioni vengono illustrate più avanti.

Contesti

La classe ContextUtil viene utilizzata per accedere al contesto dell'oggetto COM+ associato e alle relative proprietà. In questo modo è disponibile una funzionalità simile all'oggetto restituito da CoGetObjectContext nel codice non gestito. Il contesto dell'oggetto gestito associato a un componente gestito ha uno scopo diverso rispetto al contesto di oggetto non gestito associato. Ciò si manifesta scrivendo tre oggetti gestiti, uno con Transazioni necessarie (che agiscono come radice), le altre due non derivano dal componente gestito gestito (che funge da oggetti figlio che esemplificano oggetti gestiti agile dal contesto). I componenti non gestiti si comportano come se fossero componenti serviti con transazioni supportate, ovvero possono effettuare chiamate ai gestori di risorse e usare ContextUtil.SetAbort, se necessario. Quando viene creato l'oggetto radice, viene creato il contesto non gestito associato e associato al thread corrente. Quando viene eseguita una chiamata agli oggetti figlio, poiché non sono associati a un contesto non gestito, non è necessaria alcuna modifica del contesto COM+, pertanto il thread mantiene ancora l'ID del contesto non gestito della radice. Quando un oggetto figlio chiama i gestori di risorse, i gestori di risorse estraggono il contesto non gestito dal thread che esegue l'oggetto figlio, ovvero il contesto non gestito dell'oggetto radice. Basandosi su questo è pericoloso e nelle versioni future il contesto non gestito può essere unito al contesto gestito e pertanto gli oggetti figlio saranno associati a un contesto gestito potenzialmente diverso; i gestori di risorse non rileveranno il contesto dell'oggetto radice. Pertanto, l'aggiornamento a una nuova versione di .NET potrebbe interrompere il codice che dipende da questo tipo di comportamento.

Risultati delle prestazioni

In questa sezione le prestazioni del client gestito e della soluzione componente gestita del server gestito vengono confrontate con la soluzione client/server non gestita. Il caso in-process è descritto nella tabella seguente. Un ServicedComponent configurato per le transazioni è stato scritto in C# con un singolo metodo che aggiunge semplicemente numeri. Per il confronto è stata usata un'implementazione C++ corrispondente. Questo confronto mostra la differenza tra le soluzioni gestite e le soluzioni non gestite senza eseguire operazioni reali. Le attivazioni in-process sono circa 3,5 volte più lente nella soluzione gestita e le chiamate ai metodi sono circa 2 volte più costose quando è presente un cambio di contesto. Tuttavia, quando si confrontano chiamate al metodo dei componenti serviced che richiedono il cambio di contesto rispetto a quelle che non lo fanno, esistono circa 3 ordini di differenza di grandezza che indicano l'esito positivo dell'infrastruttura di intercettazione dei componenti in-process. Per le soluzioni out-of-process, le attivazioni sono circa 2 volte più costose e chiamate al metodo tra contesti circa 3 volte più costose.

La tabella 1 mostra i tempi di scalabilità per le chiamate di attivazione e metodi in-process usando soluzioni gestite e non gestite.

Tabella 1. Attivazione in-process e chiamate al metodo

  Soluzione gestita Soluzione non gestita
Activation 35 10
Chiamata al metodo cross-context-do-nothing 2 1
Chiamata al metodo cross-context-do-work 200 100

Le attivazioni sono circa un ordine di grandezza più costoso rispetto alle chiamate al metodo 'do-nothing'. L'aggiunta in alcuni lavori per ottenere semplicemente una transazione DTC (ma non fare nulla con esso) livelli i tempi di attivazione e di chiamata al metodo allo stesso ordine di grandezza. Quando la chiamata al metodo apre semplicemente una connessione di database in pool, il lavoro della chiamata al metodo è quindi un ordine di grandezza maggiore rispetto all'attivazione e alla chiamata di metodo 'do-nothing' combinata, dimostrando che il sovraccarico dell'infrastruttura del componente servizio è teorico quando il lavoro reale viene aggiunto all'esperimento.

Durata dell'oggetto

Attivazione just-in-time

Il servizio JIT (Just-In-Time) in genere non viene usato in isolamento. Viene usato in modo implicito con il servizio transazioni e spesso con il pool di oggetti. Tuttavia, questo esempio consente di evidenziare alcuni argomenti interessanti. Nel codice seguente viene scritta una classe .NET che usa solo il servizio JIT.

using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("JITDemo")]

namespace Demos
{
    [JustInTimeActivation]
    public class TestJIT : ServicedComponent
    {
       public TestJIT()
       {  // First to get called
       }
       [AutoComplete]
       public void DoWork ()
       {       // Show doneness using .. 
                  // 1. The autocomplete attribute or
                  // 2. ContextUtil.DeactivateOnReturn = true or
                  // 3. ContextUtil.SetComplete();
       } 
       public override void Dispose(bool b)
{      // Optionally override this method and do your own 
// custom Dispose logic. If b==true, Dispose() was called
// from the client, if false, the GC is cleaning up the object
}
    }
}

La classe deriva da ServicedComponent e usa l'attributo JIT per indicare il servizio specifico richiesto. Per eseguire l'override dei metodi Attiva e Disattiva nel codice non gestito, è necessaria la classe per implementare l'interfaccia IObjectControl. La classe ServicedComponent include invece metodi virtuali che possono essere sottoposti a override per gestire gli eventi Attiva e Disattiva. Tuttavia, né ServicedComponent né il relativo proxy reale, SCP, implementare IObjectControl. Invece, SCP crea un strappo proxy quando l'interfaccia IObjectControl viene richiesta da COM+. Le chiamate da COM+ sul tearoff vengono quindi inoltrate ai metodi virtuali di ServicedComponent. Il bit DeactivateOnReturn viene impostato usando l'attributo AutoComplete nel metodo, chiamando ContextUtil.SetComplete(), ContextUtil.SetAbort() o impostando ContextUtil.DeactivateOnReturn. Supponendo che il bit DeactivateOnReturn sia impostato durante ogni chiamata al metodo, la sequenza di chiamate di metodo sarebbe: il costruttore della classe, Attiva, la chiamata effettiva al metodo, Disattiva, Dispose(true) e infine il finalizzatore della classe se presente. La stessa sequenza viene ripetuta quando viene eseguita un'altra chiamata al metodo. Una buona procedura di progettazione consiste nell'eseguire l'override solo dei metodi Attiva e Disattiva per sapere quando l'oggetto viene estratto e rimesso nel pool di oggetti. La logica rimanente di Attiva e Disattiva deve essere inserita nei metodi del costruttore della classe e Dispose(bool). Il bit DisattivaOnReturn può essere impostato usando uno degli approcci seguenti:

  1. Il client usa lo stato dell'oggetto solo per una singola chiamata al metodo. Nella voce del metodo viene creato un nuovo oggetto reale e collegato a SCP. All'uscita dal metodo, l'oggetto reale viene disattivato, prima eseguendo chiamate a Dispose(true) seguito dal finalizzatore degli oggetti reali se presente. Tuttavia, il contesto COM+ associato, SCP e TP rimangono attivi. Il codice client mantiene comunque il riferimento a ciò che ritiene sia un oggetto effettivo (il proxy trasparente). La chiamata al metodo successivo effettuata dal client nello stesso riferimento comporterà la creazione di un nuovo oggetto reale e collegato al SCP per il servizio della chiamata al metodo (vedere la sezione sul pool di oggetti per rimuovere il requisito di creazione di un nuovo oggetto). Per disattivare l'oggetto reale, l'oggetto reale deve indicare la fine quando viene chiusa una chiamata al metodo. Questa operazione può essere eseguita usando:
    1. attributo AutoComplete in un metodo della classe
    2. una delle due chiamate di metodo nella classe ContextUtil, DeactivateOnReturn o SetComplete
  2. Il client effettua più chiamate di metodo sullo stesso oggetto senza disattivare l'oggetto dopo ogni chiamata al metodo impostando il bit di fine su false prima di uscire dal metodo. Ad esempio, l'ambito di un componente serviced che usa JIT a livello di modulo e che dispone di due pulsanti di modulo chiamano metodi nella stessa istanza dell'oggetto impostando in modo esplicito il bit di fine su false. A un certo punto, il bit di operazione deve essere impostato su true. Questo approccio implica che esiste un contratto tra il client e l'oggetto. Questa operazione può essere eseguita in modo implicito o esplicito dal client:
    1. Il client sa chiamare un determinato metodo sull'oggetto quando viene eseguito per disattivare l'oggetto. L'implementazione del metodo usa le idee nell'opzione 1. Il riferimento all'oggetto può essere chiamato di nuovo usando la stessa sequenza chiamante, implicando che verrà creato un nuovo oggetto reale.
    2. L'oggetto viene eliminato in modo esplicito dal client quando chiama il metodo Dispose() sull'oggetto. Dispose() è un metodo definito in ServicedComponent e chiama a turno Dispose(true), il finalizzatore della classe (se presente) e quindi rimuove il contesto COM+ associato. In questo caso, non è possibile effettuare altre chiamate di metodo sul riferimento all'oggetto. Verrà generata un'eccezione se viene tentato. Se sono presenti molti client che usano lo stesso oggetto, la chiamata di Dispose() deve essere eseguita solo quando l'ultimo client viene eseguito con l'oggetto . Tuttavia, la natura senza stato degli oggetti JIT conduce le procedure di progettazione verso un'unica istanza per ogni modello client.
    3. L'oggetto non imposta mai il bit di fine su true e il client non chiama mai Dispose(). L'oggetto reale, i proxy e il contesto vengono eliminati quando si verifica Garbage Collection. L'ordine di chiamata del metodo avviato dal GC sarebbe Disattiva, Dispose(false) quindi l'finalizzatore delle classi (se presente).

Tutti i componenti di servizio hanno un contesto COM+ associato, archiviato come riferimento in SCP (o RSCP nel caso remoto). Il riferimento viene rilasciato solo quando il GC viene eseguito o se il client chiama Dispose(). È preferibile non basarsi sulla GC per pulire il contesto: il contesto COM+ contiene un handle del sistema operativo e una certa memoria potrebbe ritardare il rilascio di questi handle fino a quando non si verifica un GC. Anche se ServicedComponent non dispone di un finalizzatore, SCP implementa un finalizzatore, il che significa che il riferimento al contesto COM+ non otterrà mai garbage collection in una prima raccolta. In effetti, quando il finalizzatore sul SCP viene chiamato, il contesto non viene ancora distrutto dal thread di finalizzatore, invece, il lavoro di distruggere i contesti viene rimosso dal thread di finalizzatore e posizionato su una coda interna. Questa operazione è stata eseguita perché è stato rilevato che il thread di finalizzatore può essere usato dal lavoro in determinati ambienti di stress in cui vengono creati rapidamente i componenti sottoposti a servizio, usati e uscire dall'ambito. Al contrario, un thread interno esegue il servizio della coda, distruggendo i vecchi contesti. Inoltre, qualsiasi thread dell'applicazione che crea un nuovo ServicedComponent tenterà innanzitutto di rimuovere un elemento dalla coda e di distruggere un contesto precedente. Pertanto, la chiamata a Dispose() dal client eliminerà il contesto COM+ prima di usare il thread client e rilascia le risorse di gestione e memoria usate dal contesto. A volte Dispose() può generare eccezioni. Un caso è se l'oggetto vive in un contesto di transazione non radice interrotto, la chiamata Dispose() può osservare un'eccezione CONTEXT_E_ABORTED. Un altro caso è illustrato nel pool di oggetti.

Da un punto di vista delle prestazioni, è preferibile non implementare un finalizzatore in una classe derivata ServicedComponent e posizionare questa logica nel metodo Dispose(bool). Anche se SCP implementa un finalizzatore, il finalizzatore dell'oggetto reale viene chiamato usando la reflection.

Una buona procedura di progettazione per l'uso di JIT consiste nel:

  • Inserire codice di attivazione e finalizzazione personalizzati nei metodi del costruttore e Dispose(bool), non per implementare un finalizzatore e usare un unico modello di chiamata indicando la fine usando l'attributo AutoComplete nel metodo.
  • Chiamare Dispose() dal client quando il client viene eseguito con l'oggetto .

La discussione ha assunto che il client sia gestito e il componente sia in-process. Quando il componente è out-of-process: (Altri dettagli sono descritti nella sezione comunicazione remota)

  • Il GC pulisce solo gli oggetti quando il tempo di lease di comunicazione remota .NET è scaduto per gli oggetti attivati dal client.
  • Come indicato in precedenza, quando si chiamano i metodi nei componenti out-of-process, DCOM viene usato per cambiare il contesto e recapitare la chiamata al metodo. Se il componente è stato disattivato in precedenza da JIT e quindi viene effettuata una chiamata a Dispose(), il contesto del server verrà immesso e l'oggetto reale verrà nuovamente creato per il servizio della chiamata DCOM e infine disattivato di nuovo. Per i componenti in-process, se l'oggetto reale è stato disattivato, non viene eseguito alcun tentativo di passare al contesto corretto prima di eseguire la manutenzione della chiamata Dispose() (che attiverebbe nuovamente il componente), ma solo il contesto viene distrutto.

Pooling di oggetti

La base del pooling di oggetti è il riutilizzo degli oggetti. Il pool di oggetti viene spesso usato con JIT. Questo vale sia per i componenti COM in pool che per i componenti .NET in pool.

using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("OPDemo")]

namespace Demos
{
[ObjectPooling(MinPoolSize=2, MaxPoolSize=50, CreationTimeOut=20)]
[JustInTimeActivation]
public class DbAccount : ServicedComponent
{
   [AutoComplete]
   public bool Perform ()
   {      // Do something
   }
   public override void Activate()
   {   // .. handle the Activate message
   }
   public override void Deactivate()
   {   // .. handle the Deactivate message
   }
   public override bool CanBePooled()
   {  // .. handle the CanBe Pooled message
      // The base implementation returns false
      return true;
   }
}
}

Come accade quando si usa JIT, il pool di oggetti può essere usato in uno dei due modi seguenti:

  1. Modello di chiamata singola. Nel codice l'oggetto viene recuperato dal pool quando il client tenta di eseguire una chiamata al metodo e viene restituito al pool in uscita dalla chiamata al metodo singolo presupponendo che JIT venga usato con il pool di oggetti e che il bit di esecuzione sia impostato su true durante la chiamata al metodo. Gli stessi approcci di chiamata singola all'uso di JIT si applicano anche qui. Il costruttore viene chiamato una sola volta quando l'oggetto viene creato e inserito nel pool. L'ordine di chiamata al metodo quando si usano oggetti JIT e in pool è: Attiva, la chiamata al metodo, Disattiva quindi CanBePooled. Se CanBePooled restituisce true, l'oggetto viene inserito nel pool (anche se il contesto rimane attivo come illustrato in precedenza). Lo stesso ordine di chiamata al metodo viene ripetuto per le chiamate di metodo successive (senza chiamare di nuovo il costruttore) dopo che un oggetto arbitrario viene estratto dal pool (i componenti di servizio non possono usare costruttori con parametri). Infine, se il client chiama Dispose() nell'oggetto in pool viene eliminato solo il contesto nel caso in-process. Nel caso out-of-process, come indicato in precedenza, la chiamata a Dispose() può riattivare l'oggetto. Se l'oggetto è in pool, deve essere ottenuto dal pool, vale a dire che Dispose() può generare un'eccezione con CO_E_ACTIVATION_TIMEOUT.
  2. Modello di chiamata multipla. Usando approcci di chiamata a più metodi simili evidenziati nel servizio JIT, l'oggetto può essere inserito di nuovo nel pool solo dopo una serie di chiamate di metodo sull'oggetto. Tuttavia, se il client non chiama Dispose e JIT non viene usato, non è possibile assicurarsi che gli oggetti figlio dell'oggetto in pool che richiedono la finalizzazione possano essere riabilitare quando l'oggetto viene inserito nel pool dal GC. Quando l'oggetto in pool è garbage collection, non vi sarebbe anche alcuna garanzia all'interno di Disattiva quali membri sono ancora validi. Nella versione successiva di .NET Framework (V1.1), canBePooled e Disattiva non vengono chiamati e l'oggetto non viene inserito nel pool. Con questo approccio esiste un modello più coerente: in Disattiva oggetti figlio sono attivi, in Dispose(), gli oggetti figlio non sono garantiti per essere attivi. Pertanto, è fondamentale che Dispose() venga chiamato per gli oggetti in pool che non usano JIT, altrimenti l'oggetto non verrà restituito al pool.

È accettabile che un amministratore modifichi le dimensioni e i timeout del pool dopo la distribuzione e la registrazione dell'assembly. Le modifiche delle dimensioni del pool hanno effetto quando il processo viene riavviato. In Windows XP o versione migliore, le dimensioni del pool si applicano a ogni dominio dell'applicazione all'interno del processo. In Windows 2000 le dimensioni del pool vengono elaborate a livello di oggetto con pool che risiede nel dominio dell'applicazione predefinito, il che significa che se un oggetto in pool è richiesto da un altro dominio dell'app nello stesso processo, il client comunica in modo efficace attraverso il dominio dell'app all'oggetto in pool. Una realizzazione di questo è l'uso di oggetti .NET in pool definiti in un'applicazione di libreria COM+ dall'interno di un'applicazione ASP.NET in cui ogni vroot IIS è ospitato in domini applicazione separati.

I componenti serviced non possono usare costruttori con parametri.

Sicurezza

Sicurezza dall'accesso di codice (CAS, Code Access Security)

La sicurezza di .NET Framework consente al codice di accedere alle risorse solo se dispone dell'autorizzazione a tale scopo. Per esprimere questa operazione, .NET Framework usa il concetto di autorizzazioni, che rappresentano il diritto per il codice di accedere alle risorse protette. Il codice richiede le autorizzazioni necessarie. .NET Framework fornisce classi di autorizzazione di accesso al codice. In alternativa, è possibile scrivere classi di autorizzazioni personalizzate. Queste autorizzazioni possono essere usate per indicare a .NET Framework quali operazioni devono essere consentite dal codice e per indicare quali chiamate del codice devono essere autorizzate a eseguire. Qualsiasi percorso di codice tramite System.EnterpriseServices richiede autorizzazioni di codice non gestite.

La sicurezza di accesso al codice in .NET è più utile nelle applicazioni in cui il codice viene scaricato dal Web e l'autore potrebbe non essere completamente attendibile. In genere, le applicazioni che usano componenti di servizio sono completamente attendibili, richiedono la sicurezza per il flusso tra più processi e abilitare la configurazione dei ruoli in fase di distribuzione. Queste sono funzionalità esposte dalla sicurezza basata sui ruoli COM+.

Qualsiasi percorso di codice tramite System.EnterpriseServices richiede autorizzazioni di codice non gestite. Questo implica quanto segue:

  • L'autorizzazione del codice non gestita è necessaria per attivare ed eseguire chiamate tra contesti nei componenti di servizio.
  • Se un riferimento a un componente serviced viene passato al codice non attendibile, i metodi definiti in ServicedComponent non possono essere chiamati dal codice non attendibile. Tuttavia, i metodi personalizzati definiti in una classe derivata da ServicedComponent possono essere chiamati dal codice non attendibile in alcune circostanze: le chiamate dal codice non attendibile possono essere eseguite su tali metodi personalizzati che non richiedono il cambio di contesto, i servizi di intercettazione e se l'implementazione del metodo non effettua chiamate ai membri di System.EnterpriseServices.

Inoltre, in .NET versione 1 lo stack di sicurezza non viene copiato quando viene eseguita un'opzione di thread in modo che le autorizzazioni di sicurezza personalizzate non vengano usate all'interno di componenti di servizio.

sicurezza Role-Based (RBS)

System.EnterpriseServices fornisce servizi di sicurezza agli oggetti .NET che mirrora la funzionalità dei meccanismi di sicurezza COM+. Quando un'applicazione server COM+ viene usata per ospitare i componenti, le funzionalità RBS richiedono che il protocollo di trasporto DCOM venga usato per attivare i componenti da un client remoto. Altre informazioni sulla comunicazione remota vengono fornite nella sezione successiva. Il contesto e l'identità delle chiamate di sicurezza in COM+ sono pertanto disponibili per il codice gestito. Inoltre, CoImpersonateClient, CoInitializeSecurity e CoRevertClient sono chiamate familiari in genere usate sul lato server, mentre CoSetProxyBlanket viene generalmente usato sul lato client.

Alcune impostazioni di sicurezza non vengono archiviate nei metadati usando attributi, ad esempio aggiungendo utenti ai ruoli e impostando l'identità di sicurezza del processo. Tuttavia, gli attributi a livello di assembly possono essere usati per configurare ciò che viene visualizzato nella scheda sicurezza di COM+ Explorer per un'applicazione server COM+:

  • Abilitazione dell'autorizzazione per l'applicazione (ApplicationAccessControlAttribute(bool)). È necessario che sia true per supportare RBS.

  • Livello di sicurezza (ApplicationAccessControlAttribute(AccessChecksLevelOption)). Se impostato su AccessChecksLevelOption.Application, gli utenti assegnati ai ruoli nell'applicazione vengono aggiunti al descrittore di sicurezza del processo e al controllo dei ruoli con granularità fine al componente, al metodo e ai livelli di interfaccia vengono disattivati. I controlli di sicurezza vengono quindi eseguiti solo a livello di applicazione e le applicazioni di libreria si basano sul processo host per la sicurezza a livello di processo. Se l'attributo è impostato su AccessChecksLevelOption.ApplicationComponent, gli utenti assegnati ai ruoli nell'applicazione vengono aggiunti al descrittore di sicurezza del processo e i controlli di sicurezza basati su ruoli vengono eseguiti nell'applicazione. Inoltre, i controlli di accesso devono essere abilitati per ogni componente che richiede RBS applicando l'attributo ComponentAccessControl nella classe . In un'applicazione di libreria i controlli di sicurezza basati sui ruoli vengono eseguiti come se fosse un'applicazione server. La proprietà di sicurezza è inclusa nel contesto per tutti gli oggetti all'interno dell'applicazione e il contesto della chiamata di sicurezza è disponibile. Se un oggetto ha una configurazione incompatibile con il contesto del relativo creatore, viene attivata nel proprio contesto. La sicurezza basata sul ruolo a livello di codice si basa sulla disponibilità del contesto di chiamata di sicurezza.

    Per qualsiasi controllo di accesso significativo al lavoro per le applicazioni di libreria COM+, scegliere di eseguire controlli di accesso a livello di processo e componente.

  • Le selezioni di rappresentazione e autenticazione corrispondono alle proprietà ImpersonationLevel e Authentication dell'attributo ApplicationAccessControl.

    L'attributo SecurityRole può essere applicato all'assembly, alla classe o al livello di metodo. Quando applicato a livello di assembly, gli utenti in tale ruolo possono attivare qualsiasi componente nell'applicazione. Se applicato al livello di classe, gli utenti in tale ruolo possono anche chiamare qualsiasi metodo nel componente. I ruoli a livello di applicazione e classe possono essere configurati nei metadati o in modo amministrativo accedendo al catalogo COM+.

    Configurazione di RBS a livello di assembly usando i metadati:

    [assembly: ApplicationAccessControl(true,
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    // adds NTAuthority\everyone to this role
    [assembly:SecurityRole("TestRole1",true)]
    // add users to roles administratively
    [assembly:SecurityRole("TestRole2")]
    

    Configurazione di RBS a livello di classe nei metadati:

    [assembly: ApplicationAccessControl(true,
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    …
    [ComponentAccessControl()]
    [SecurityRole("TestRole2")]
    public class Foo : ServicedComponent
    {
    public void Method1() {}
    }
    

    È possibile configurare RBS a livello di assembly o classe in modo amministrativo perché tali entità esistono nel catalogo COM+ dopo la registrazione dell'assembly. Tuttavia, come illustrato in precedenza, i metodi di classe non vengono visualizzati nel catalogo COM+. Per configurare RBS nei metodi, la classe deve implementare metodi di un'interfaccia e deve usare l'attributo SecureMethod a livello di classe o SecureMethod o SecurityRole a livello di metodo. Inoltre, gli attributi devono essere visualizzati nell'implementazione del metodo di classe, non nel metodo di interfaccia nella definizione dell'interfaccia.

  • Il modo più semplice per usare RBS nei metodi consiste nell'applicare l'attributo SecureMethod a livello di classe e quindi configurare i ruoli (in modo amministrativo o inserendo l'attributo SecurityRole sui metodi).

    [assembly: ApplicationAccessControl(true,
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    Interface IFoo
    {
    void Method1();
    void Method2();
    }
    [ComponentAccessControl()] 
    [SecureMethod]
    public class Foo : ServicedComponent, IFoo
    {
    // Add roles to this method administratively
    public void Method1() {} 
    // "RoleX" is added to the catalog for this method
    SecurityRole("RoleX")
    public void Method2() {}
    }
    

    L'uso di SecureMethod a livello di classe consente a tutti i metodi in tutte le interfacce della classe di essere configurati con ruoli nel catalogo COM+ in modo amministrativo. Se la classe implementa due interfacce ognuna con lo stesso nome del metodo e i ruoli vengono configurati in modo amministrativo, i ruoli devono essere configurati in entrambi i metodi come vengono visualizzati nel catalogo COM+ (a meno che la classe implementi il metodo specifico, ad esempio IFoo.Method1). Tuttavia, se l'attributo SecurityRole viene usato nel metodo di classe, tutti i metodi con lo stesso nome del metodo vengono configurati automaticamente con tale ruolo quando l'assembly viene registrato.

  • L'attributo SecureMethod può essere inserito anche a livello di metodo.

    [assembly: ApplicationAccessControl(true, 
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    Interface IFoo
    {
       void Method1();
       void Method2();
    }
    [ComponentAccessControl()] 
    public class Foo : ServicedComponent, IFoo
    {
    // Add roles to this method administratively
    [SecureMethod]  // Or use SecurityRole (translates to
      SecureMethod++)
       public void Method1() {}
       public void Method2() {}
    }
    

    Nell'esempio, IFoo e entrambi i metodi vengono visualizzati nel catalogo COM+ e pertanto i ruoli possono essere configurati in entrambi i metodi in modo amministrativo, tuttavia, il livello di metodo RBS viene applicato solo su Method1. Usare SecureMethod o SecurityRole in tutti i metodi necessari per partecipare alla sicurezza RBS a livello di metodo o posizionare SecureMethod a livello di classe come descritto in precedenza.

Ogni volta che viene configurato RBS a livello di metodo, è necessario il ruolo Marshaller: quando vengono eseguite chiamate al metodo e RBS non è configurato nei metodi, l'infrastruttura del componente serviced effettua chiamate su IRemoteDispatch. Quando vengono eseguite chiamate al metodo e RBS viene configurato su metodi (quando è presente l'attributo SecureMethod), la chiamata al metodo viene eseguita usando DCOM usando l'interfaccia associata al metodo . Pertanto, DCOM garantisce che RBS venga applicato a livello di metodo. Tuttavia, come illustrato nelle sezioni Attivazione e intercettazione, l'interoperabilità COM e RSCP eseguirà quindi chiamate su IManagedObject (per consentire agli attivatori remoti di eseguire il marshalling del riferimento nello spazio) e IServicedComponentInfo (per eseguire query sull'oggetto remoto). Queste interfacce sono associate ai componenti di servizio. Poiché il componente è configurato per eseguire controlli a livello di metodo, è necessario associare un ruolo a queste interfacce se l'infrastruttura deve eseguire correttamente queste chiamate.

Pertanto, un ruolo Marshaller viene aggiunto all'applicazione quando l'assembly è registrato e gli utenti devono quindi essere aggiunti in modo amministrativo a questo ruolo. Più spesso tutti gli utenti dell'applicazione vengono aggiunti a questo ruolo. Ciò è leggermente diverso da COM+ non gestito, in cui la configurazione di RBS nei metodi non richiede questo passaggio di configurazione aggiuntivo. L'aggiunta automatica di "Tutti" a questo ruolo durante la registrazione è un potenziale buco di sicurezza perché chiunque potrebbe ora essere in grado di attivare (ma non chiamare) componenti in cui prima potrebbero non avere i diritti per attivarli. Il ruolo Marshaller viene aggiunto anche all'interfaccia IDisposable per consentire ai client di eliminare l'oggetto. Un'alternativa al ruolo Marshaller consiste nell'aggiungere i ruoli pertinenti a ognuna delle tre interfacce indicate.

Componenti remoti

La classe ServicedComponent contiene MarshallalByRefObject nell'albero di ereditarietà e quindi può essere accessibile dai client remoti. Esistono molte varianti di come esporre i componenti serviced in remoto. I componenti serviced possono essere accessibili in remoto usando:

  • Il canale HTTP con componenti serviti chiamati da o scritti in ASP.NET offre opzioni di sicurezza e crittografia ottimali, oltre a scalabilità e prestazioni note. Se usato con SOAP, esistono altre opzioni di interoperabilità. I componenti serviced possono essere ospitati in IIS/ASP.NET come applicazione della libreria COM+. Se viene usata un'applicazione server COM+, l'host IIS/ASP.NET può accedere ai componenti usando DCOM.
  • Un modo alternativo per esporre un componente serviced come endpoint SOAP viene illustrato in COM+ Web Services: il Check-Box Route to XML Web Services.
  • DCOM quando i componenti serviced sono ospitati in Dllhost. Questa opzione offre prestazioni e sicurezza ottimali e la possibilità di passare contesti di servizio tra computer. La principale domanda di progettazione quando si sceglie una tecnologia di comunicazione remota deve essere se i servizi devono scorrere tra i computer. Ad esempio, all'interno di una server farm in cui viene creata una transazione in un computer ed è necessario che la transazione continui in un altro computer, DCOM è l'unico protocollo che può essere usato per ottenere questo risultato. Tuttavia, se i client devono semplicemente chiamare un servicedComponent remoto, gli approcci http o endpoint SOAP sono alternative valide.
  • Un canale di comunicazione remota .NET (ad esempio, un canale TCP o personalizzato). Per usare il canale TCP è necessario un processo in ascolto su un socket. In generale, un processo personalizzato viene usato per ascoltare un socket e quindi ospitare componenti gestiti come libreria COM+ o applicazione server. In alternativa, Dllhost può essere usato come listener. L'approccio è meno probabile che venga usato e richiederebbe la scrittura di un listener socket personalizzato con prestazioni, scalabilità e sicurezza comprovate. Pertanto, le soluzioni ASP.NET o DCOM sono gli approcci migliori per la maggior parte dei progetti.

Per accedere a un componente serviced in remoto usando DCOM e ospitato in Dllhost, assicurarsi prima di tutto che l'assembly sia registrato in un'applicazione server COM+ e inserita nella gaC nel computer server. Usare quindi la struttura di esportazione dell'applicazione COM+ per creare un file MSI per un proxy dell'applicazione. Installare il proxy dell'applicazione nel client. Incorporato nel proxy dell'applicazione è l'assembly gestito. Il programma di installazione registra anche l'assembly e lo inserisce nel computer client. Di conseguenza:

  • .NET Framework deve essere installato nel client e nel server. Questa operazione è necessaria nel computer client anche se solo i client non gestiti accederanno ai componenti gestiti remoti. Nelle piattaforme Windows 2000 è necessario anche Service Pack 3.
  • Dopo aver disinstallato il proxy, l'assembly deve essere rimosso anche dalla GAC.

L'infrastruttura dopo l'attivazione del componente server nel codice gestito dal lato client è illustrata nella figura 6.

L'uso di DCOM implica che CLR è ospitato in Dllhost, il che significa che il file di configurazione dell'applicazione dllhost.exe.config risiede nella directory system32. Ciò implica anche che il file di configurazione si applica a tutti i processi Dllhost nel computer. Nella versione successiva di .NET Framework (V1.1), la directory radice dell'applicazione COM+ può essere impostata nell'applicazione COM+ e tale directory viene usata per individuare i file di configurazione e gli assembly per l'applicazione.

Per gli oggetti attivati dal client, ogni volta che viene richiesto l'URI di un oggetto, viene creato un lease di durata per tale oggetto. Come descritto in precedenza nella sezione Attivazione, viene richiesto un URI dal proxy del componente con servizio remoto. Ciò può verificarsi anche quando un componente in-process serviced esistente viene sottoposto a marshalling a un processo remoto. Un URI viene richiesto ogni volta che un oggetto MBR viene sottoposto a marshalling da .NET all'esterno di un dominio applicazione. L'URI viene usato per garantire che le identità degli oggetti in .NET siano univoche e per impedire il concatenamento del proxy. Pertanto, quando un client gestito attiva un componente con servizio remoto, i tempi di lease vengono utilizzati nell'oggetto server. Si noti che i client non gestiti non dispongono di un proxy del componente gestito remoto sul lato client e pertanto non richiedono l'URI dell'oggetto. I client non gestiti usano invece DCOM per garantire l'identità dell'oggetto. Pertanto, i tempi di lease sui componenti serviti non vengono usati quando vengono attivati da client non gestiti.

Quando i tempi di lease sono coinvolti nei componenti serviti, è consigliabile impostare i valori di timeout InitialLeaseTime e RenewOnCallTime su un valore ridotto, possibilmente anche di dimensioni ridotte di 10 secondi. I componenti serviti vengono eliminati definitivamente usando Dispose() o con GC per pulire gli oggetti. Quando viene chiamato Dispose(), il proxy del componente con servizio remoto rilascia il riferimento presente nel proxy DCOM e quindi si rende disponibile per il GC successivo. L'oggetto server elaborerà la chiamata Dispose (o viene creato un nuovo oggetto server per il servizio della chiamata remota a Dispose()), eliminerà il contesto COM+ associato e quindi si renderà disponibile per il GC successivo, ma solo quando si è verificato il timeout del lease. Quando il client non chiama Dispose(), il server dovrà prima attendere che il GC lato client rilasci il riferimento al proxy DCOM e solo se stesso e il contesto COM+ disponibile per il GC successivo dopo la scadenza del lease. Chiamare quindi Dispose() e, inoltre, ridurre il tempo di lease predefinito. Anche se il client rimane attivo e il tempo di lease scade, il riferimento DCOM all'oggetto server manterrà attivo l'oggetto server. Tuttavia, il riferimento DCOM non viene sempre usato per mantenere attivo il componente serviced. Quando il client accede all'oggetto tramite un canale di comunicazione remota CLR o servizi SOAP COM+, solo il riferimento sicuro dovuto al lease manterrà attivo il componente sottoposto a servizio.

Conclusione

Questo articolo ha illustrato solo alcuni dei servizi disponibili per il codice gestito. Tutti i servizi COM+ sono disponibili per il codice gestito, ad esempio i livelli di isolamento delle transazioni, l'inizializzazione dei processi, i servizi senza componenti e il riciclo dei processi. .NET Framework offre ora l'accesso uguale a tutti i servizi COM+ in modo coerente e logico. Inoltre, una serie di parti innovative di .NET Framework, ad esempio ASP.NET, Microsoft ADO.NET e messaggistica, si integrano profondamente con .NET Enterprise Services, usando servizi come transazioni e pool di oggetti. Questa integrazione offre un'architettura e un modello di programmazione coerenti. Lo spazio dei nomi System.EnterpriseServices fornisce il modello di programmazione per aggiungere servizi alle classi gestite.