Présentation des services d’entreprise (COM+) dans .NET

 

Shannon Pahl
Microsoft Corporation

Avril 2002

Résumé : Fournit des détails techniques sur l’intégration des services Microsoft .NET et COM+ et décrit les services disponibles pour le code managé. (26 pages imprimées)

Contenu

Introduction
Transactions
Déploiement
Composants pris en charge
Durées de vie des objets
Sécurité
Composants distants
Conclusion

Introduction

Cet article nécessite une certaine connaissance des services Microsoft.NET® Framework et COM+. Une connaissance des services d’entreprise n’est pas nécessaire, mais serait utile. Pour obtenir de bonnes informations sur ces sujets, reportez-vous à :

COM fournit un moyen d’écrire des applications basées sur des composants. Il est bien connu que le travail de plomberie requis pour écrire des composants COM est important et répétitif. COM+ ne concerne pas tellement une nouvelle version de COM ; COM+ fournit plutôt une infrastructure de services pour les composants. Les composants sont générés, puis installés dans les applications COM+ afin de créer des applications serveur évolutives qui atteignent un débit élevé avec facilité de déploiement. (Si un composant n’a pas besoin d’utiliser de services, il ne doit pas être placé dans une application COM+). La scalabilité et le débit sont obtenus en concevant des applications dès le départ pour utiliser des services tels que les transactions, le regroupement d’objets et la sémantique d’activité.

Le .NET Framework offre un autre moyen d’écrire des applications basées sur des composants et présente les avantages par rapport au modèle de programmation COM d’une meilleure prise en charge des outils, du Common Language Runtime (CLR) et d’une syntaxe de codage beaucoup plus facile. L’infrastructure des services COM+ est accessible à partir de code managé et non managé. Les services du code non managé sont appelés services COM+. Dans .NET, ces services sont appelés Services d’entreprise. La dérivation d’une classe à partir de ServicedComponent indique que des services seront nécessaires pour un composant. (Si un composant n’a pas besoin d’utiliser de services, il ne doit pas dériver de ServicedComponent). La prise en charge des outils a été améliorée pour permettre aux programmeurs d’écrire des applications serveur, mais les mêmes problèmes de scalabilité et de débit sont toujours dans le domaine des bonnes pratiques de programmation. L’idée de base des services estd’esign pour le débit et la scalabilité dès le départ et de tirer parti des services d’entreprise pour implémenter facilement ces modèles de conception le cas échéant.

On pourrait faire valoir que la conception de l’infrastructure des services a peu à voir avec les composants COM ou même les composants : les services COM+ peuvent désormais être appliqués aux composants COM, aux composants .NET et même à d’autres entités qui ne sont pas considérées comme des composants, comme des pages ASP ou des blocs de code arbitraires (voir la fonctionnalité Services sans composants COM+ sur Microsoft Windows® XP).

Tous les services COM+ disponibles aujourd’hui sont disponibles pour les objets .NET et COM. Certains de ces services incluent, les transactions, le regroupement d’objets et les chaînes de construction, JIT, synchronisation, sécurité basée sur les rôles, CRM et BYOT. Pour obtenir la liste complète des services sur Microsoft Windows 2000, consultez Services fournis par COM+ dans le Kit de développement logiciel (SDK) de plateforme. Microsoft Windows XP inclut une nouvelle version de COM+, à savoir COM+ 1.5, qui comporte des services supplémentaires qui peuvent également être utilisés avec des composants .NET.

Transactions

Pour écrire des applications managées qui utilisent des services, les classes nécessitant des services doivent dériver de ServicedComponent et utiliser différents attributs personnalisés pour spécifier les services réels requis. Cette section présente ces concepts et la façon dont ils affectent l’écriture de code managé. Une explication plus détaillée est fournie dans les sections ultérieures.

Supposons qu’un compte de classe a été écrit (le code réel est répertorié ultérieurement) et qu’il se trouve dans l’assembly BankComponent. Cette classe peut être utilisée comme suit :

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

Pour générer le client, la référence doit être ajoutée à l’espace de noms BankComponent. En outre, une référence doit être ajoutée pour l’assembly System.EnterpriseServices: dans l’espace de noms BankComponentClient, le client appelle Dispose() et le constructeur ServicedComponent, qui sont des méthodes définies dans System.EnterpriseServices, et non dans l’assembly contenant BankComponent. Il s’agit d’une exigence .NET générale lorsqu’une classe dérivée ne remplace pas toutes les méthodes de classe de base.

Le code du serveur BankComponent montre l’implémentation de la classe Account dans .NET, qui utilise des transactions. Le compte de classe dérive de la classe System.EnterpriseServices.ServicedComponent. L’attribut Transaction marque la classe comme nécessitant une transaction. Les services Synchronisation et JIT sont configurés automatiquement, car l’attribut Transaction est utilisé. L’attribut AutoComplete est utilisé pour spécifier que le runtime doit appeler automatiquement la fonction SetAbort pour la transaction si une exception non prise en charge est levée pendant l’exécution de la méthode ; sinon, une fonction SetComplete est appelée. L’attribut ApplicationName associe cet assembly à une application COM+ qui stocke les données de configuration du service pour cette application. D’autres modifications requises pour cette classe sont mises en évidence dans le code.

Serveur BankComponent

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.
            }
      }
}

Le code dans l’espace de noms BankComponent Server montre à quel point il est facile d’utiliser les services COM+ dans .NET. Vous trouverez ci-dessous un résumé du processus complet du codage au déploiement :

  1. Écrivez l’assembly de serveur.

  2. Générez l’assembly :

    1. Signez l’assembly. Un fichier de clé peut être généré une fois pour le projet. Il n’est pas nécessaire d’en générer un pour chaque compilation. Vous pouvez créer une clé à l’aide de l’invite de commandes Microsoft .NET et sn.exe :

      sn –k Demos.snk
      
    2. Compilez le code. Une référence doit être ajoutée pour System.EnterpriseServices.

  3. Déployez l’application.

    Un assembly qui utilise des composants pris en charge doit être inscrit auprès du catalogue COM+. La classe ServicedComponent et les attributs personnalisés sont les deux concepts clés pour accéder aux services COM+ à partir de code managé. La configuration du service est stockée dans le catalogue COM+. Les objets résident et s’exécutent dans le CLR. L’objet managé et son contexte COM+ associé sont représentés dans la figure 1 et deviendront plus clairs dans les deux sections suivantes.

    Figure 1. Services associés aux composants managés

    Avec les composants COM+, vous devez configurer le catalogue manuellement, mais avec les composants serviceés, le catalogue peut être mis à jour en fonction des attributs dans le code. Un assembly peut être inscrit explicitement à l’aide de l’outil en ligne de commande regsvcs.exe ou en écrivant des scripts qui accèdent à une API managée. Plus d’informations sont fournies dans la section détails du déploiement ci-dessous. Pendant le développement, le déploiement XCopy est fourni à titre pratique en copiant simplement l’assembly dans le répertoire de l’application. Chaque fois qu’une application cliente crée des instances de classes dérivées de ServicedComponent, le runtime détecte s’il a déjà inscrit l’assembly dans une application COM+. S’il n’a pas été inscrit, l’annuaire local est recherché pour l’assembly et, s’il est trouvé, tous les composants gérés dans l’assembly sont inscrits dans une application COM+ et l’activation peut ensuite continuer. C’est ce que l’on appelle l’inscription paresseuse, mais elle ne fonctionne pas dans tous les scénarios. Par instance, les assemblys marqués en tant qu’application serveur COM+ nécessitent une inscription explicite (voir ci-dessous), et l’inscription différée ne fonctionne pas pour les clients non managés appelant des composants gérés. L’inscription différée est utile pendant le temps de développement. Sinon, utilisez des scripts, du code ou des regSvcs pour inscrire l’assembly.

  4. Placez éventuellement l’assembly dans le GAC. Pour plus d’informations, consultez la section déploiement.

  5. Exécutez le client.

Déploiement

Les attributs personnalisés sont l’un des deux concepts clés de l’accès aux services COM+ à partir de code managé. Les attributs personnalisés sont utilisés pour spécifier les services requis, tels que l’attribut personnalisé Transaction dans la liste de code précédente. Ces attributs stockent les options de configuration d’un service dans les métadonnées de l’assembly. Les attributs personnalisés sont utilisés en faisant en sorte que du code charge l’assembly et à l’aide de la réflexion, créez des instances des attributs et appelez des méthodes sur l’attribut pour extraire la configuration du service stockée dans l’attribut. Les informations peuvent ensuite être écrites dans le catalogue COM+. Le code qui effectue ces étapes et d’autres est contenu dans EnterpriseServices.RegistrationHelper. Pour faciliter le processus d’inscription, toutes les formes d’inscription utilisent le composant EnterpriseServices.RegistrationHelper. Ce composant est accessible en tant que classe managée et objet COM.

Figure 2 : Inscription de composants pris en charge

D’un point de vue conceptuel, RegistrationHelper effectue les étapes suivantes :

  • Utilise RegistrationServices.RegisterAssembly pour inscrire l’assembly dans le Registre. Par conséquent, les classes apparaissent dans le Registre en tant que composants COM écrits en code managé et ont la clé InprocServer32 pointant vers mscoree.dll. Si une classe managée n’implémente aucune interface, les méthodes publiques de la classe n’apparaissent pas dans le catalogue COM+, sauf si l’attribut ClassInterfaceAttribute est utilisé. Cela signifie que la configuration de service associée au niveau de méthode ne peut pas être stockée dans le catalogue. Toutefois, certains services COM+ peuvent être configurés au niveau de la méthode et exiger que le composant expose une interface telle qu’elle est affichée dans le catalogue COM+. Par exemple, la sécurité basée sur les rôles COM+ au niveau de la méthode nécessite qu’un composant implémente une interface afin de configurer le service. Vous trouverez plus d’informations sur ce problème dans la section sécurité.
  • Génère une bibliothèque de types COM à partir de l’assembly à l’aide de TypeLibConverter. ConvertAssemblyToTypeLib.
  • Inscrit la bibliothèque de types. Jusqu’à présent, cela est très similaire à RegAsm.exe /tlb.
  • Recherche ou crée une application COM+. Le nom est extrait de l’attribut ApplicationName, du nom de l’assembly ou du nom/GUID de l’application fourni.
  • Utilise la bibliothèque de types pour configurer l’application COM+ à l’aide des API d’administration COM+.
  • Passe en revue tous les attributs personnalisés et utilise IConfigurationAttribute pour écrire des données de configuration pour le service particulier dans le catalogue COM+.

RegistrationHelper tente d’effectuer ces étapes dans une transaction à l’aide de RegistrationHelperTx, une classe au sein d’une application COM+ créée lors de l’installation de .NET. Par conséquent, si l’inscription échoue, le catalogue ET le registre COM+ sont restaurés à leur état d’origine. Actuellement, les bibliothèques de types générées restent toutefois sur disque (ou dans le GAC si l’assembly était dans le GAC). Si l’assembly inscrit fait référence à d’autres assemblys qui utilisent également les services COM+, tous les assemblys de l’graphe des dépendances subiront les mêmes étapes que celles répertoriées ci-dessus.

Étant donné que RegistrationHelper accède au catalogue COM+, il nécessite des autorisations de code non managées et des droits d’administrateur sur l’ordinateur. Par conséquent, il en va de même pour les clients de RegistrationHelper, à savoir : inscription différée, RegSvcs ou vos scripts/code. Cela implique également que le code téléchargé à partir d’Internet ou stocké sur un partage réseau ne peut pas être inscrit.

Il est possible de coder des combinaisons d’attributs incompatibles, telles que l’exigence d’une transaction et la définition de la synchronisation sur désactivée. Ces combinaisons sont actuellement détectées au moment de l’inscription lors de leur écriture dans le catalogue COM+ et non au moment de la compilation. Certains attributs ont des dépendances implicites sur d’autres attributs, par instance, lorsque vous utilisez l’attribut Transaction uniquement, cela revient à utiliser les attributs Transaction, JustInTimeActivation et Synchronization. Lorsqu’un composant managé est inscrit, les valeurs par défaut du catalogue COM+ sont utilisées, sauf si des attributs sont utilisés pour remplacer les valeurs par défaut « non configurées ». Par instance, si un composant est inscrit et qu’aucun attribut Transaction n’est spécifié, la valeur par défaut non configurée du paramètre de transaction dans le catalogue est définie sur TransactionOption.Disabled. Cette approche permet à un développeur de supprimer un attribut du code si le composant n’en a plus besoin, puis lorsque l’assembly est à nouveau inscrit, l’entrée de catalogue pour la transaction est correctement réinitialisée. Une liste détaillée de ces valeurs par défaut non configurées est spécifiée dans la documentation en ligne. Les valeurs configurées par défaut sont les valeurs par défaut dans les paramètres d’un attribut, pour instance, l’utilisation de l’attribut [Transaction] indique TransactionOption.Required.

Étant donné que les données de configuration des services sur les classes managées sont stockées dans le catalogue COM+, certaines entrées de catalogue peuvent également être modifiées administrativement après l’inscription d’un assembly. Certains services ne doivent pas être modifiés de cette manière. Par instance, la désactivation du service de transaction dans le catalogue peut entraîner un fonctionnement incorrect du code. Les paramètres spécifiques au déploiement, tels que les chaînes de construction d’objet et les rôles de sécurité, peuvent être manipulés après l’inscription. Le déploiement XCopy d’assemblys contenant des composants pris en charge peut ne pas suffire lorsque les paramètres de post-inscription sont définis. La fonctionnalité d’importation et d’exportation d’application COM+ permet de distribuer l’état actuel de l’application. Pour plus d’informations sur l’importation et l’exportation, consultez la section communication à distance.

Dans certains cas, le catalogue n’est pas consulté pour les données de configuration, mais il est extrait uniquement des métadonnées de l’assembly. Les cas concernent la saisie semi-automatique, L’accès JIT, le regroupement d’objets (bien que la taille du pool soit extraite du catalogue) et l’attribut de méthode sécurisée. Vous trouverez plus d’informations sur ce problème dans les sections décrivant ces services.

Le processus d’inscription d’un assembly génère automatiquement les GUID requis par COM+. Si l’assembly n’est pas signé, les GUID sont générés uniquement en fonction des noms de type et d’espace de noms. Par conséquent, des GUID non uniques peuvent être générés si l’assembly n’est pas signé. Un sort similaire est imposé aux assemblys .NET qui n’utilisent même pas les services COM+ mais nécessitent des noms de type uniques. Par conséquent, lesssemblies qui utilisent des services COM+ doivent être signés. L’inscription échoue si les assemblys ne sont pas signés. L’inscription implique également qu’une classe .NET qui utilise les services COM+ dispose d’un magasin de données de configuration global. Bien qu’il soit possible de copier des assemblys privés dans plusieurs répertoires d’applications, toutes ces applications font finalement référence à une seule donnée de configuration pour un composant pris en charge. Par conséquent, la modification des données de configuration dans le catalogue COM+ affecte toutes les applications qui utilisent la classe . Cela est évident avec plusieurs vroots dans une configuration Microsoft ASP.NET qui comprennent toutes une copie du même assembly qui utilise des composants pris en charge. Une façon d’avoir plusieurs configurations pour la même application COM+ consiste à utiliser des partitions COM+ sur Microsoft Windows .NET. Pour utiliser le service de partitions COM+ dans .NET, n’utilisez pas l’attribut ApplicationID : pour installer le même composant dans plusieurs partitions, COM+ nécessite des ID d’application uniques.

En général, le GAC est utilisé chaque fois qu’un client doit accéder à des assemblys qui ne se trouvent pas dans le même répertoire que le répertoire de l’application cliente ou si l’assembly est chargé dans un autre processus, qui ne se trouve pas dans le répertoire du client. D’un point de vue conceptuel, les assemblys privés qui utilisent des composants pris en charge sont en fait des assemblys partagés, c’est-à-dire leur utilisation des données de configuration qu’ils ont partagées. Si ApplicationActivationOption est défini sur bibliothèque, il est possible d’utiliser des transactions sur une classe dans un assembly et d’utiliser cet assembly dans un client si tous les assemblys sont chargés à partir du même répertoire. Lorsqu’un assembly qui utilise ApplicationActivationOption est défini sur serveur, l’assembly est chargé par dllhost.exe, qui ne se trouve probablement pas dans le même répertoire que le client. Les assemblys qui utilisent des composants pris en charge dans les applications serveur COM+ doivent être placés dans le GAC. Les assemblys qui utilisent des composants pris en charge dans les applications de bibliothèques COM+ n’ont peut-être pas besoin d’être placés dans le GAC (sauf s’ils se trouvent dans des répertoires différents). La seule exception est lors de l’hébergement avec ASP.NET: les assemblys ne doivent pas être placés dans le GAC pour permettre au cliché instantané de fonctionner correctement.

Pour supprimer une application .NET qui utilise des composants pris en charge, supprimez l’assembly du GAC (s’il a été inscrit auprès du GAC), désinscrivez l’assembly de COM+ à l’aide de regsvcs.exe puis supprimez l’assembly et les bibliothèques de types associées.

Contrôle de version

Il est possible de corriger les GUID requis par COM+ à l’aide d’un attribut GUID. Toutefois, il est recommandé d’utiliser le contrôle de version au lieu d’utiliser explicitement des GUID. Chaque fois qu’une nouvelle signature de méthode est créée ou que des classes sont décorées avec des attributs de service différents, le numéro de version principale ou secondaire de l’assembly doit être incrémenté. L’inscription doit être effectuée une fois pour une version particulière. Lors de l’inscription d’une nouvelle version de l’assembly, de nouveaux GUID sont générés pour cette version et les composants sont inscrits dans la même application COM+ à l’aide du même nom de composant. Les composants apparaissent donc plusieurs fois dans l’application COM+. Toutefois, chaque composant a des ID uniques donnés par les GUID. Chaque instance fait référence à une version particulière du composant. Cela est souvent remarqué lors de la génération d’applications .NET à l’aide de Microsoft Visual Studio® .NET. L’environnement ajoute l’attribut [assembly : AssemblyVersion(« 1.0.* »)] à un projet. Chaque nouvelle build du projet génère un nouveau numéro de build et, par conséquent, de nouveaux GUID sont générés lorsque l’assembly est réinscrit. Par conséquent, il peut être préférable d’incrémenter manuellement les numéros de build le cas échéant. Les clients sont liés à un assembly à l’aide de la stratégie de version DU CLR et, par conséquent, la version correcte de la classe dans l’application COM+ sera utilisée. Voici quelques scénarios côte à côte lors de l’écriture d’assemblys (serveurs managés) qui utilisent des composants gérés : (certains aspects de l’activation utilisés ci-dessous seront décrits dans la section suivante)

  • Client managé, serveur managé, aucun GUID fixe utilisé dans l’assembly.
  • Le client chargera l’assembly spécifié par la stratégie de version.
  • Client managé, serveur managé, GUID fixes utilisés.
  • Si le client active une classe et utilise la stratégie de version pour accéder à une ancienne version de l’assembly, le GUID fixe dans le code est utilisé lors de l’activation pour extraire les informations de service du catalogue. Par conséquent, les informations du dernier assembly inscrit utilisant ce GUID seront utilisées pour créer l’objet, qui peut en fait être la version la plus récente et, par conséquent, il y aurait une exception de conversion de type lors de la tentative de conversion de l’objet réellement créé (v2) vers la référence dans le code (v1).
  • Client managé, serveur managé, aucun GUID fixe, modifiez uniquement le numéro de build.
  • Bien que de nouveaux GUID soient générés, la bibliothèque de types aura toujours le même numéro de version, car les bibliothèques de types n’ont que deux numéros pour la version. Cela peut toujours fonctionner, mais si la version 2 est installée sur la version 1, la version 1 est désinstallée, la bibliothèque de types pour la version 2 est désinscrit. Solution 1 : la prochaine version du .NET Framework (V1.1) résout ce problème en permettant à la bibliothèque de types d’être versionnée indépendamment de l’assembly. Cela implique que lors de la modification du numéro de version de l’assembly, la version de la bibliothèque de types doit être modifiée. Solution 2 : utiliser uniquement les numéros de version principale et secondaire.
  • Client non managé, serveur managé, aucun GUID fixe utilisé.
    • Le client utilisera un GUID pour créer le composant. L’interopérabilité résout le GUID en un nom, puis la stratégie de version est appliquée. Si les versions 1 et 2 d’un assembly se trouvent sur un ordinateur et que la stratégie est utilisée pour accéder à la version 2, le client non managé obtient la version 2.
    • Installez la version 1, installez la version 2, désinstallez la version 1. À présent, le client ne peut pas créer le composant, sauf s’il existe une stratégie de version à rediriger vers la version 2. En outre, les entrées de Registre doivent exister pour les informations d’inscription de la version 1. Une façon de créer des informations de Registre pour une version désinstallée 1 consiste à utiliser la fonctionnalité d’alias COM+ sur Windows XP.

Le contrôle de version s’applique à tous les composants de la même application COM+, c’est-à-dire qu’il n’existe aucun moyen automatique de version de l’application elle-même. Par instance, les rôles sur l’application ne peuvent pas être versionnés à l’aide de la stratégie de version. Utilisez l’attribut nom de l’application pour effectuer le contrôle de version de l’application.

Composants pris en charge

Activation

L’infrastructure Enterprise Services est fondée sur le concept de contexte. Un contexte est un environnement pour les objets ayant des exigences d’exécution similaires. Les services peuvent être appliqués pendant l’activation et/ou pendant l’interception des appels de méthode. Bien que les services COM+ soient écrits en code non managé, l’intégration des services COM+ à .NET est beaucoup plus profonde que la simple utilisation de la technologie d’interopérabilité COM dans .NET. Sans dérivation de ServicedComponent, le processus d’inscription n’aurait pas l’effet souhaité.

Les composants pris en charge peuvent être activés et hébergés dans diverses combinaisons. Comme illustré dans la figure 3, cette discussion fait référence à trois cas: in-process (même domaine d’application), inter-domaine d’application (même processus) et inter-processus activations. L’importance de ces cas est les limites qui sont franchies lors des appels sur les composants. L’activation in-process donne lieu à une limite inter-contexte potentielle, le cas inter-domaine d’application a à la fois des limites de domaine d’application croisées et inter-contextes, tandis que le cas inter-processus traite des limites entre ordinateurs/processus et entre contextes.

Figure 3. Hôtes d’activation pour les composants pris en charge

L’implémentation des composants pris en charge s’appuie sur la communication à distance .NET, qui fournit un mécanisme extensible pour brancher des services écrits dans du code non managé ou non managé. Les composants pris en charge dérivent de ContextBoundObject et implémentent différentes interfaces, telles que IDisposable. La chaîne d’activation dans le CLR est facilement personnalisée à l’aide d’attributs personnalisés dérivés de ProxyAttribute. L’interception peut être personnalisée en écrivant des proxys réels personnalisés. Lorsqu’une nouvelle classe dérivée d’un composant service est requise, la chaîne d’activation est personnalisée afin que l’appel d’activation appelle réellement un wrapper C++ managé pour CoCreateInstance. Cela permet à COM+ de configurer les contextes et services non managés en fonction des informations stockées dans le catalogue COM+ à partir d’un assembly précédemment inscrit. Il s’agit également de l’étape où l’inscription différée est implémentée. Pendant l’inscription de l’assembly, la clé InprocServer32 pointe vers mscoree.dll, redirigeant ainsi com+ CreateInstance vers le runtime pour créer l’objet managé réel. Par conséquent, pendant l’activation, un objet proxy réel personnalisé est créé. La version in-process de ce proxy est appelée proxy de composant service ou SCP. Ceci est illustré dans la figure 4.

Figure 4. Chemin d’activation

Le chemin d’accès de retour de l’appel d’activation marshale les références managées à partir du code managé, via COM+ non managé et de nouveau dans le code managé (chemin inverse de la ligne 1 de la figure 4). Selon l’emplacement où l’objet réel a été créé, le côté client dé marshale la référence dans le formulaire approprié. Dans le cas d’une activation in-process, la figure 5 indique que la référence est non délimitée en tant que référence directe au proxy transparent (TP). Les références inter-domaines d’application sont non délimitées en tant que proxy de communication à distance .NET. Les références inter-processus ou inter-ordinateurs (figure 6) nécessitent davantage de démarshalation : COM Interop effectue des appels sur IManagedObject implémenté par ServicedComponent pendant l’activation et le démarshalation. Le proxy de composant de service distant (RSCP) effectue des appels sur IServicedComponentInfo pendant l’activation pour obtenir l’URI de l’objet serveur, ce qui implique que deux appels distants sont effectués pendant l’activation. Lorsque la sécurité basée sur les rôles COM+ est requise au niveau de la méthode, ces interfaces doivent être associées à un rôle pour que le démarshalation réussisse lorsque l’infrastructure effectue des appels sur ces interfaces. La section sécurité décrit les implications de l’activation et du marshaling inter-processus sur la configuration de la sécurité basée sur les rôles.

Figure 5. Infrastructure pour les appels de processus in

Figure 6. Infrastructure pour les appels hors processus

La chaîne d’activation a donc été personnalisée pour créer un proxy réel personnalisé (utilisé pour l’interception) et pour créer les contextes non managés, ce qui laisse COM+ avec uniquement l’infrastructure de contexte nécessaire pour effectuer la sémantique des services d’interception. Le contexte COM+ est désormais associé à un objet managé, et non à un objet COM.

Interception

La figure 7 illustre l’infrastructure des appels de méthode in-process. Le proxy personnalisé (SCP) permet aux appels managés d’être interceptés. Pendant l’activation, l’ID de contexte COM+ est stocké dans le scp. Quand un objet managé appelle un composant géré, l’ID de contexte stocké dans le scp cible est comparé à l’ID de contexte du contexte actuel. Si les ID de contexte sont identiques, l’appel est exécuté directement sur l’objet réel. Lorsque les ID de contexte sont différents, le SCP effectue un appel à COM+ pour changer les contextes et rendre le service d’entrée de l’appel de méthode. Pour les appels in-process, cela est similaire à AppDomain.DoCallBack, mais appDomain étant COM+. La fonction « DoCallBack » entre d’abord dans COM+ (étape 2 de la figure 7), qui change le contexte et restitue le service, puis la fonction de rappel appelle sur le SCP. Le scp effectue le marshaling des données et appelle la méthode sur l’objet réel. Lorsque la méthode se ferme, le chemin d’accès de retour permet à COM+ d’afficher la sémantique pour quitter un appel de méthode (étape 5 de la figure 7) . COM+ est utilisé uniquement pour rendre le service. Le marshaling des données et l’appel de méthode sont effectués dans le runtime .NET*,* de sorte que la conversion de types tels que String en BSTR n’est pas nécessaire lors de l’appel de méthodes. Le marshaling des données serait nécessaire si l’interopérabilité COM était utilisée pour les appels in-process*.* L’appel pour rendre le service dans du code non managé n’est donc pas un appel COM Interop pour les appels in-process.

Figure 7. Infrastructure pour les appels de processus in

Les appels sur les méthodes statiques ne sont pas transférés aux proxys transparents et réels. Par conséquent, les méthodes statiques ne peuvent pas utiliser les services d’interception ; au lieu de cela, ils sont appelés dans le contexte du client. Les méthodes internes sont appelées dans le contexte approprié, ce qui signifie qu’un client appelant une méthode interne sur un objet configuré pour une nouvelle transaction prendra part à une nouvelle transaction. Toutefois, étant donné que les services au niveau de la méthode nécessitent une interface dans le catalogue COM+ (plus d’informations sur cette rubrique et sur la sécurité), les méthodes internes ne peuvent pas être configurées pour les services de niveau méthode. Les services peuvent s’appliquer aux propriétés, mais les attributs au niveau de la méthode (comme autocomplétion) doivent être placés sur les méthodes getter et setter individuellement.

L’attribut AutoComplete est un moyen pratique d’utiliser des transactions sans écrire de code pour accéder au service. Vous pouvez également utiliser ContextUtil.SetAbort ou ContextUtil.SetComplete. Ce service est configurable dans l’Explorateur COM+ en définissant une case à cocher sur les propriétés de la méthode . Toutefois, les objets managés n’ont pas besoin d’implémenter d’interfaces. Cela est également vrai pour les composants pris en charge. Lorsque la méthode n’est pas déclarée sur une interface, la configuration des services au niveau de la méthode ne peut pas être écrite dans le catalogue lors de l’inscription ; la configuration ne peut être stockée que dans des métadonnées. Lorsqu’il n’existe aucune interface pour la méthode, le changement de contexte est effectué à partir du SCP à l’aide des informations de configuration stockées sur IRemoteDispatch.RemoteDispatchAutoDone lorsque l’attribut AutoComplete est présent. Quand la saisie semi-automatique n’est pas présente, IRemoteDispatch.RemoteDispatchNotAutoDone est utilisé. IRemoteDispatch est une interface implémentée par ServicedComponent. Les clients non managés peuvent uniquement appeler des composants gérés qui n’ont pas d’interfaces à l’aide d’IDispatch (liaison tardive) et, par conséquent, la sémantique de saisie semi-automatique ne peut pas être appliquée en raison de l’absence d’un proxy réel dans ce cas. Même lorsque des interfaces sont utilisées, la configuration de la saisie semi-automatique est toujours pilotée par les métadonnées des clients managés. L’appel de méthode DCOM est effectué sur RemoteDispatchAutoDone uniquement dans le cas hors processus. Les composants hors processus n’utilisent pas le mécanisme DoCallBack. À la place, DCOM peut être utilisé pour remettre l’appel et rendre le service. Si la méthode se trouve sur une interface, la méthode d’interface sur le composant de service distant est appelée à l’aide de DCOM. Sinon, l’appel est distribué à l’interface IRemoteDispatch sur ServicedComponent. Cela signifie que même les appels comme Dispose() sont appelés par le biais de DCOM, dont les implications sont abordées plus loin.

Contextes

La classe ContextUtil est utilisée pour accéder au contexte d’objet COM+ associé et à ses propriétés. Cela fournit des fonctionnalités similaires à l’objet retourné par CoGetObjectContext dans du code non managé. Le contexte de l’objet managé associé à un composant pris en charge a un objectif différent de celui du contexte d’objet non managé associé. Cela se manifeste par l’écriture de trois objets managés, l’un avec transactions requises (agissant en tant que racine), les deux autres ne dérivant pas du composant pris en charge (agissant en tant qu’objets enfants illustrant des objets managés agiles dans le contexte). Les composants non gérés se comportent comme s’ils étaient des composants pris en charge avec les transactions prises en charge, c’est-à-dire qu’ils peuvent effectuer des appels aux gestionnaires de ressources et utiliser ContextUtil.SetAbort si nécessaire. Lorsque l’objet racine est créé, le contexte non managé associé est créé et associé au thread actuel. Lorsqu’un appel aux objets enfants est effectué, étant donné qu’ils ne sont pas associés à un contexte non managé, aucune modification de contexte COM+ n’est nécessaire. Par conséquent, le thread conserve toujours l’ID de contexte non managé de la racine. Lorsqu’un objet enfant appelle sur des gestionnaires de ressources, les gestionnaires de ressources extraient à leur tour le contexte non managé du thread exécutant l’objet enfant, qui est le contexte non managé de l’objet racine. S’appuyer sur cela est dangereux et dans les versions ultérieures, le contexte non managé peut fusionner avec le contexte managé et, par conséquent, les objets enfants seront associés à un contexte managé potentiellement différent ; les gestionnaires de ressources ne récupèrent pas le contexte de l’objet racine. Par conséquent, la mise à niveau vers une nouvelle version de .NET peut interrompre le code qui dépend de ce type de comportement.

Résultats des performances

Dans cette section, les performances de la solution de composant client managé et serveur géré sont comparées à la solution client/serveur non managée. Le cas in-process est décrit dans le tableau suivant. Un ServicedComponent configuré pour les transactions a été écrit en C# avec une méthode unique qui ajoute simplement des nombres. Une implémentation C++ correspondante a été utilisée pour la comparaison. Cette comparaison montre la différence entre les solutions gérées et les solutions non managées sans effectuer de travail réel. Les activations in-process sont environ 3,5 fois plus lentes dans la solution managée et les appels de méthode sont environ 2 fois plus coûteux en cas de changement de contexte. Toutefois, lors de la comparaison des appels de méthode de composant service qui nécessitent un changement de contexte et ceux qui ne le font pas, il existe environ 3 ordres de différence de grandeur indiquant la réussite de l’infrastructure d’interception des composants dans le processus. Pour les solutions hors processus, les activations sont environ 2 fois plus coûteuses et les appels de méthode inter-contexte environ 3 fois plus coûteux.

Le tableau 1 présente les temps mis à l’échelle pour l’activation in-process et les appels de méthodes à l’aide de solutions managées et non managées.

Tableau 1. Activation in-process et appels de méthode

  Solution managée Solution non managée
Activation 35 10
Appel de méthode inter-contexte-do-nothing 2 1
Appel de méthode inter-contexte-do-work 200 100

Les activations sont d’environ un ordre de grandeur plus coûteux que les appels de méthode à la méthode « do-nothing ». L’ajout d’un travail pour obtenir simplement une transaction DTC (mais ne rien faire avec elle) permet de mettre à niveau les temps d’activation et d’appel de méthode au même ordre de grandeur. Lorsque l’appel de méthode ouvre simplement une connexion de base de données mise en pool, le travail de l’appel de méthode est alors d’un ordre de grandeur supérieur à celui de l’activation et de l’appel de méthode « do-nothing » combinés, ce qui prouve que la surcharge de l’infrastructure des composants pris en charge est théorique lorsque du travail réel est ajouté à l’expérience.

Durées de vie des objets

Activation juste-à-temps

Le service juste-à-temps (JIT) n’est généralement pas utilisé isolément. Il est utilisé implicitement avec le service de transaction et le plus souvent avec le regroupement d’objets. Toutefois, cet exemple permet de mettre en évidence certaines rubriques intéressantes. Dans le code ci-dessous, une classe .NET est écrite qui utilise uniquement le service 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 dérive de ServicedComponent et utilise l’attribut JIT pour indiquer le service spécifique requis. Pour remplacer les méthodes Activate et Deactivate dans du code non managé, la classe est nécessaire pour implémenter l’interface IObjectControl. La classe ServicedComponent a plutôt des méthodes virtuelles qui peuvent être remplacées pour gérer les événements Activate et Deactivate. Toutefois, ni ServicedComponent ni son proxy réel, SCP, n’implémentent IObjectControl. Au lieu de cela, le SCP crée une coupure de proxy lorsque l’interface IObjectControl est demandée par COM+. Les appels de COM+ sur la déchirure sont ensuite transférés aux méthodes virtuelles de ServicedComponent. Le bit DeactivateOnReturn est défini à l’aide de l’attribut AutoComplete sur la méthode, en appelant ContextUtil.SetComplete(), ContextUtil.SetAbort() ou en définissant ContextUtil.DeactivateOnReturn. En supposant que le bit DeactivateOnReturn est défini pendant chaque appel de méthode, la séquence d’appels de méthode serait : le constructeur de la classe, Activate, l’appel de méthode réel, Deactivate, Dispose(true) et finalement le finaliseur de la classe s’il en existe un. La même séquence est répétée lorsqu’un autre appel de méthode est effectué. Une bonne pratique de conception consiste à remplacer uniquement les méthodes Activate et Deactivate pour savoir quand l’objet est retiré et remis dans le pool d’objets. La logique restante de Activate et Deactivate doit être placée dans le constructeur de la classe et les méthodes Dispose(bool). Le bit DeactivateOnReturn peut être défini à l’aide de l’une des approches suivantes :

  1. Le client utilise l’état de l’objet uniquement pour un seul appel de méthode. Lors de l’entrée dans la méthode, un nouvel objet réel est créé et attaché au SCP. Lors de la sortie de la méthode, l’objet réel est désactivé, d’abord en effectuant des appels à Dispose(true), puis au finaliseur d’objets réels s’il en existe un. Toutefois, le contexte COM+, SCP et TP associés restent actifs. Le code client conserve toujours sa référence à ce qu’il croit être un objet réel (le proxy transparent). L’appel de méthode suivant effectué par le client sur la même référence entraîne la création et l’attachement d’un nouvel objet réel au SCP afin de traiter l’appel de méthode (voir la section sur le regroupement d’objets pour supprimer l’exigence de création d’un nouvel objet). Pour désactiver l’objet réel, l’objet réel doit indiquer le caractère terminé lorsqu’un appel de méthode se termine. Pour ce faire, utilisez :
    1. Attribut AutoComplete sur une méthode de la classe
    2. l’un des deux appels de méthode sur la classe ContextUtil, DeactivateOnReturn ou SetComplete
  2. Le client effectue plusieurs appels de méthode sur le même objet sans désactiver l’objet après chaque appel de méthode en définissant le bit doneness sur false avant de quitter la méthode. Par exemple, l’étendue d’un composant serviceé qui utilise L’accès JIT au niveau du formulaire et le fait que deux boutons de formulaire appellent des méthodes sur le même objet instance en définissant explicitement le bit doneness sur false. À un moment donné, le bit doneness doit être défini sur true. Cette approche implique l’existence d’un contrat entre le client et l’objet. Cela peut être effectué implicitement ou explicitement par le client :
    1. Le client sait qu’il faut appeler une certaine méthode sur l’objet lorsque cela est effectué afin de désactiver l’objet. L’implémentation de la méthode utilise les idées de l’option 1. La référence d’objet peut être appelée à nouveau à l’aide de la même séquence appelante, ce qui implique qu’un nouvel objet réel sera créé.
    2. L’objet est explicitement détruit par le client lorsqu’il appelle la méthode Dispose() sur l’objet . Dispose() est une méthode définie sur ServicedComponent et appelle à son tour Dispose(true), le finaliseur de la classe (le cas échéant), puis supprime le contexte COM+ associé. Dans ce cas, aucun autre appel de méthode ne peut être effectué sur la référence d’objet. Une exception est levée si cette opération est tentée. Si de nombreux clients utilisent le même objet, l’appel de Dispose() ne doit être effectué que lorsque le dernier client est terminé avec l’objet . Toutefois, la nature sans état des objets JIT conduit les pratiques de conception à une seule instance par modèle client.
    3. L’objet ne définit jamais son bit doneness sur true et le client n’appelle jamais Dispose(). L’objet, les proxys et le contexte réels sont détruits lorsque le garbage collection a lieu. L’ordre d’appel de méthode lancé par le GC serait Deactivate, Dispose(false), puis le finaliseur de classes (le cas échéant).

Tous les composants pris en charge ont un contexte COM+ associé, qui est stocké en tant que référence dans le SCP (ou RSCP dans le cas distant). La référence est publiée uniquement lorsque le gc a lieu ou si le client appelle Dispose(). Il est préférable de ne pas s’appuyer sur le gc pour propre le contexte : le contexte COM+ conserve un handle de système d’exploitation et une certaine mémoire pouvant retarder la publication de ces handles jusqu’à ce qu’un GC se produise. En outre, bien que ServicedComponent ne dispose pas d’un finaliseur, le SCP implémente un finaliseur, ce qui signifie que la référence de contexte COM+ n’est jamais récupérée sur une première collection. En fait, lorsque le finaliseur sur le SCP est finalement appelé, le contexte n’est toujours pas détruit par le thread du finaliseur. Au lieu de cela, le travail de destruction des contextes est supprimé du thread du finaliseur et placé dans une file d’attente interne. Cela a été fait parce qu’il a été constaté que le thread du finaliseur peut être consommé par le travail dans certains environnements stressants où les composants pris en charge sont rapidement créés, utilisés et hors de portée. Au lieu de cela, un thread interne dessert la file d’attente, détruisant les anciens contextes. En outre, tout thread d’application qui crée un nouveau ServicedComponent tente d’abord de retirer un élément de la file d’attente et de détruire un ancien contexte. Par conséquent, l’appel de Dispose() à partir du client supprime le contexte COM+ plus tôt à l’aide du thread client et libère les ressources de handle et de mémoire consommées par le contexte. Parfois, Dispose() peut lever des exceptions. L’un des cas est si l’objet se trouve dans un contexte de transaction non racine qui a été abandonné, l’appel Dispose() peut observer une exception CONTEXT_E_ABORTED. Un autre cas est expliqué dans le regroupement d’objets.

Du point de vue des performances, il est préférable de ne pas implémenter de finaliseur dans une classe dérivée ServicedComponent et de placer cette logique dans la méthode Dispose(bool). Bien que le SCP implémente un finaliseur, le finaliseur de l’objet réel est appelé à l’aide de la réflexion.

Une bonne pratique de conception pour l’utilisation de JIT consiste à :

  • Placez du code d’activation et de finalisation personnalisé dans le constructeur et les méthodes Dispose(bool), non pour implémenter un finaliseur et utiliser un modèle d’appel unique en indiquant l’exécution à l’aide de l’attribut AutoComplete sur la méthode .
  • Appelez Dispose() à partir du client lorsque le client a terminé avec l’objet .

La discussion a supposé que le client est géré et que le composant est in-process. Lorsque le composant est hors processus : (plus de détails sont décrits dans la section communication à distance)

  • Le gc n’propre les objets que lorsque leur durée de bail de communication à distance .NET a expiré pour les objets activés par le client.
  • Comme indiqué précédemment, lors de l’appel de méthodes sur des composants hors processus, DCOM est utilisé pour changer le contexte et remettre l’appel de méthode. Si le composant a été désactivé par JIT précédemment et qu’un appel à Dispose() est effectué, le contexte du serveur est entré et l’objet réel est recréé pour traiter l’appel DCOM, puis désactivé à nouveau. Pour les composants in-process, si l’objet réel a été désactivé, aucune tentative n’est effectuée pour basculer vers le contexte approprié avant la maintenance de l’appel Dispose() (qui réactiverait le composant), à la place, seul le contexte est détruit.

Regroupement d’objets

Le principe de base du regroupement d’objets est la réutilisation d’objets. Le regroupement d’objets est le plus souvent utilisé avec JIT. Cela est vrai à la fois pour les composants COM mis en pool et les composants .NET mis en 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;
   }
}
}

Comme c’est le cas lors de l’utilisation de JIT, le regroupement d’objets peut être utilisé de l’une des deux manières suivantes :

  1. Modèle d’appel unique. Dans le code, l’objet est récupéré à partir du pool lorsque le client tente d’effectuer un appel de méthode et est retourné au pool à la sortie de l’appel de méthode unique, en supposant que JIT est utilisé avec le regroupement d’objets et que le bit doneness a la valeur true pendant l’appel de la méthode. Les mêmes approches d’appel unique pour l’utilisation de JIT s’appliquent également ici. Le constructeur n’est appelé qu’une seule fois lorsque l’objet est créé et placé dans le pool. L’ordre d’appel de méthode lors de l’utilisation d’objets JIT et mis en pool est : Activate, l’appel de méthode, Deactivate puis CanBePooled. Si CanBePooled retourne true, l’objet est remis dans le pool (bien que le contexte reste actif, comme indiqué précédemment). Le même ordre d’appel de méthode est répété pour les appels de méthode suivants (sans appeler à nouveau le constructeur) après l’extraction d’un objet arbitraire du pool (les composants pris en charge ne peuvent pas utiliser de constructeurs paramétrables). Enfin, si le client appelle Dispose() sur l’objet mis en pool, seul le contexte est détruit dans le cas in-process. Dans le cas hors processus, comme indiqué précédemment, l’appel à Dispose() peut réactiver l’objet. Si l’objet est mis en pool, un objet doit être obtenu à partir du pool, ce qui signifie que Dispose() peut lever une exception avec CO_E_ACTIVATION_TIMEOUT.
  2. Modèle d’appel multiple. À l’aide d’approches d’appel de méthodes multiples similaires mises en surbrillance dans le service JIT, l’objet ne peut être remis dans le pool qu’après un certain nombre d’appels de méthode sur l’objet. Toutefois, si le client n’appelle pas Dispose et que JIT n’est pas utilisé, il n’existe aucun moyen de garantir que les objets enfants de l’objet mis en pool qui nécessitent une finalisation peuvent être ressuscités lorsque l’objet est remis dans le pool par le GC. Lorsque l’objet mis en pool est récupéré par le garbage collection, il n’existe aucune garantie dans Deactivate que les membres sont toujours valides. Dans la version suivante du .NET Framework (V1.1), canBePooled et Deactivate ne sont pas appelés et l’objet n’est pas remis dans le pool. Avec cette approche, il existe un modèle plus cohérent : dans Désactiver les objets enfants sont actifs, dans Dispose(), il n’est pas garanti que les objets enfants soient actifs. Par conséquent, il est essentiel que Dispose() soit appelé pour les objets mis en pool qui n’utilisent pas JIT, sinon l’objet n’est pas retourné au pool.

Il est acceptable pour un administrateur de modifier la taille du pool et les délais d’expiration après le déploiement et l’inscription de l’assembly. Les modifications de taille du pool prennent effet lorsque le processus est redémarré. Sur Windows XP ou une version ultérieure, la taille du pool s’applique à chaque domaine d’application au sein du processus. Sur Windows 2000, la taille du pool est large avec l’objet mis en pool résidant dans le domaine d’application par défaut, ce qui signifie que si un objet mis en pool est requis à partir d’un autre domaine d’application au sein du même processus, le client communique efficacement entre le domaine d’application et l’objet mis en pool. L’une des réalisations consiste à utiliser des objets .NET mis en pool définis dans une application de bibliothèque COM+ à partir d’une application ASP.NET où chaque racine virtuelle IIS est hébergée dans des domaines d’application distincts.

Les composants pris en charge ne peuvent pas utiliser de constructeurs paramétrables.

Sécurité

Sécurité d'accès du code

La sécurité .NET Framework permet au code d’accéder aux ressources uniquement s’il est autorisé à le faire. Pour exprimer cela, le .NET Framework utilise le concept d’autorisations, qui représentent le droit pour le code d’accéder aux ressources protégées. Le code demande les autorisations dont il a besoin. Le .NET Framework fournit des classes d’autorisation d’accès au code. Vous pouvez également écrire des classes d’autorisation personnalisées. Ces autorisations peuvent être utilisées pour indiquer au .NET Framework ce que le code doit être autorisé à faire et pour indiquer ce que les appelants du code doivent être autorisés à faire. Tout chemin de code via System.EnterpriseServices demande des autorisations de code non managées.

La sécurité d’accès au code dans .NET est particulièrement utile dans les applications où le code est téléchargé à partir du web et où l’auteur peut ne pas être entièrement approuvé. En règle générale, les applications qui utilisent des composants pris en charge sont entièrement fiables et nécessitent une sécurité pour circuler entre plusieurs processus et permettre la configuration des rôles au moment du déploiement. Il s’agit de fonctionnalités exposées par la sécurité basée sur le rôle COM+.

Tout chemin de code via System.EnterpriseServices nécessite des autorisations de code non managées. Cela implique ce qui suit :

  • L’autorisation de code non managé est requise pour activer et effectuer des appels inter-contextes sur les composants gérés.
  • Si une référence à un composant pris en charge est passée à du code non approuvé, les méthodes définies sur ServicedComponent ne peuvent pas être appelées à partir du code non approuvé. Toutefois, les méthodes personnalisées définies sur une classe dérivée de ServicedComponent peuvent être appelées à partir de code non approuvé dans certaines circonstances : des appels à partir de code non approuvé peuvent être effectués sur les méthodes personnalisées qui ne nécessitent pas de basculement de contexte, de services d’interception et si l’implémentation de la méthode n’effectue pas d’appels aux membres de System.EnterpriseServices.

En outre, dans .NET version 1, la pile de sécurité n’est pas copiée lorsqu’un commutateur de thread est effectué, de sorte que les autorisations de sécurité personnalisées ne doivent pas être utilisées dans les composants pris en charge.

Role-Based Security (RBS)

System.EnterpriseServices fournit des services de sécurité aux objets .NET qui miroir les fonctionnalités des mécanismes de sécurité COM+. Lorsqu’une application serveur COM+ est utilisée pour héberger les composants, les fonctionnalités RBS nécessitent que le protocole de transport DCOM soit utilisé pour activer les composants à partir d’un client distant. Plus d’informations sur la communication à distance sont fournies dans la section suivante. Le contexte et l’identité de l’appel de sécurité dans COM+ sont donc disponibles pour le code managé. En outre, CoImpersonateClient, CoInitializeSecurity et CoRevertClient sont des appels familiers généralement utilisés côté serveur, tandis que CoSetProxyBlanket est généralement utilisé côté client.

Certains paramètres de sécurité ne sont pas stockés dans les métadonnées à l’aide d’attributs, pour instance, l’ajout d’utilisateurs à des rôles et la définition de l’identité de sécurité du processus. Toutefois, les attributs au niveau de l’assembly peuvent être utilisés pour configurer ce qui apparaît dans l’onglet Sécurité de l’explorateur COM+ pour une application serveur COM+ :

  • Activation de l’autorisation pour l’application (ApplicationAccessControlAttribute(bool)). Cela doit être vrai pour prendre en charge RBS.

  • Niveau de sécurité (ApplicationAccessControlAttribute(AccessChecksLevelOption)). Si la valeur est AccessChecksLevelOption.Application, les utilisateurs affectés à des rôles dans l’application sont ajoutés au descripteur de sécurité du processus et la vérification fine des rôles au niveau du composant, de la méthode et de l’interface est désactivée. Les vérifications de sécurité sont donc effectuées uniquement au niveau de l’application et les applications de bibliothèque s’appuient sur le processus hôte pour la sécurité au niveau du processus. Si l’attribut est défini sur AccessChecksLevelOption.ApplicationComponent, les utilisateurs affectés à des rôles dans l’application sont ajoutés au descripteur de sécurité du processus et des vérifications de sécurité basées sur les rôles sont effectuées sur l’application. En outre, les vérifications d’accès doivent également être activées pour chaque composant nécessitant RBS en appliquant l’attribut ComponentAccessControl sur la classe . Dans une application de bibliothèque, les vérifications de sécurité basées sur les rôles sont effectuées comme s’il s’agissait d’une application serveur. La propriété de sécurité est incluse dans le contexte pour tous les objets de l’application et le contexte d’appel de sécurité est disponible. Si un objet a une configuration incompatible avec le contexte de son créateur, il est activé dans son propre contexte. La sécurité basée sur le rôle par programme s’appuie sur la disponibilité du contexte d’appel de sécurité.

    Pour que toute vérification d’accès significative fonctionne pour les applications de bibliothèque COM+, choisissez d’effectuer des vérifications d’accès au niveau du processus et du composant.

  • Les sélections d’emprunt d’identité et d’authentification correspondent aux propriétés ImpersonationLevel et Authentication de l’attribut ApplicationAccessControl.

    L’attribut SecurityRole peut être appliqué au niveau de l’assembly, de la classe ou de la méthode. Lorsqu’il est appliqué au niveau de l’assembly, les utilisateurs dans ce rôle peuvent activer n’importe quel composant dans l’application. Lorsqu’il est appliqué au niveau de la classe, les utilisateurs dans ce rôle peuvent, en outre, appeler n’importe quelle méthode sur le composant. Les rôles au niveau de l’application et de la classe peuvent être configurés dans les métadonnées ou administrativement en accédant au catalogue COM+.

    Configuration de RBS au niveau de l’assembly à l’aide de métadonnées :

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

    Configuration de RBS au niveau de la classe dans les métadonnées :

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

    RBS au niveau de l’assembly ou de la classe peut être configuré administrativement, car ces entités existent dans le catalogue COM+ après l’inscription de l’assembly. Toutefois, comme indiqué précédemment, les méthodes de classe n’apparaissent pas dans le catalogue COM+. Pour configurer RBS sur des méthodes, la classe doit implémenter des méthodes d’une interface et utiliser l’attribut SecureMethod au niveau de la classe, ou SecureMethod ou SecurityRole au niveau de la méthode. En outre, les attributs doivent apparaître sur l’implémentation de la méthode de classe, et non sur la méthode d’interface dans la définition de l’interface.

  • Le moyen le plus simple d’utiliser RBS sur des méthodes consiste à appliquer l’attribut SecureMethod au niveau de la classe, puis à configurer des rôles (administrativement ou en plaçant l’attribut SecurityRole sur les méthodes).

    [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’utilisation de SecureMethod au niveau de la classe permet de configurer administrativement toutes les méthodes de toutes les interfaces de la classe avec des rôles dans le catalogue COM+. Si la classe implémente deux interfaces portant chacune le même nom de méthode et que les rôles sont configurés administrativement, les rôles doivent être configurés sur les deux méthodes telles qu’elles apparaissent dans le catalogue COM+ (sauf si la classe implémente la méthode spécifique, pour instance, IFoo.Method1). Toutefois, si l’attribut SecurityRole est utilisé sur la méthode de classe, toutes les méthodes portant le même nom de méthode sont automatiquement configurées avec ce rôle lorsque l’assembly est inscrit.

  • L’attribut SecureMethod peut également être placé au niveau de la méthode.

    [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() {}
    }
    

    Dans l’exemple, IFoo et les deux méthodes s’affichent dans le catalogue COM+. Par conséquent, les rôles peuvent être configurés sur l’une ou l’autre méthode de manière administrative. Toutefois, rbS au niveau de la méthode est appliqué uniquement sur Method1. Utilisez SecureMethod ou SecurityRole sur toutes les méthodes qui seront requises pour participer à la sécurité RBS au niveau de la méthode ou placez SecureMethod au niveau de la classe comme indiqué précédemment.

Chaque fois que RBS est configuré au niveau de la méthode, le rôle Marshaller est requis : lorsque des appels de méthode sont effectués et que RBS n’est pas configuré sur des méthodes, l’infrastructure de composant service effectue des appels sur IRemoteDispatch. Lorsque des appels de méthode sont effectués et que RBS est configuré sur des méthodes (lorsque l’attribut SecureMethod est présent), l’appel de méthode est effectué à l’aide de DCOM à l’aide de l’interface associée à la méthode. Par conséquent, DCOM garantit que RBS est appliqué au niveau de la méthode. Toutefois, comme indiqué dans les sections Activation et Interception, l’interopérabilité COM et le RSCP effectuent ensuite des appels sur IManagedObject (afin de permettre aux activateurs distants de marshaler la référence dans leur espace) et IServicedComponentInfo (pour interroger l’objet distant). Ces interfaces sont associées à des composants pris en charge. Étant donné que le composant est configuré pour effectuer des vérifications au niveau de la méthode, un rôle doit être associé à ces interfaces si l’infrastructure doit effectuer ces appels avec succès.

Par conséquent, un rôle Marshaller est ajouté à l’application lorsque l’assembly est inscrit et que les utilisateurs doivent ensuite être ajoutés administrativement à ce rôle. Le plus souvent, tous les utilisateurs de l’application sont ajoutés à ce rôle. Cela diffère quelque peu de com+ non managé où la configuration de RBS sur des méthodes ne nécessite pas cette étape de configuration supplémentaire. L’ajout automatique de « Tout le monde » à ce rôle lors de l’inscription est un problème de sécurité potentiel, car tout le monde serait désormais en mesure d’activer (mais pas d’appeler) des composants là où il n’avait peut-être pas eu les droits pour les activer. Le rôle Marshaller est également ajouté à l’interface IDisposable pour permettre aux clients de supprimer l’objet. Une alternative au rôle Marshaller consiste pour les utilisateurs à ajouter les rôles pertinents à chacune des trois interfaces mentionnées.

Composants distants

La classe ServicedComponent contient MarshalByRefObject dans son arborescence d’héritage et est donc accessible à partir de clients distants. Il existe de nombreuses variantes de la façon d’exposer des composants serviceés à distance. Les composants pris en charge sont accessibles à distance à l’aide des éléments suivants :

  • Le canal HTTP avec des composants pris en charge appelés ou écrits dans ASP.NET offre de bonnes options de sécurité et de chiffrement, ainsi qu’une scalabilité et des performances connues. Lorsqu’il est utilisé avec SOAP, d’autres options d’interopérabilité existent. Les composants gérés peuvent être hébergés dans IIS/ASP.NET en tant qu’application de bibliothèque COM+. Si une application serveur COM+ est utilisée, l’hôte IIS/ASP.NET peut accéder aux composants à l’aide de DCOM.
  • Une autre façon d’exposer un composant entretenu en tant que point de terminaison SOAP est décrite dans Services web COM+ : Le Check-Box Route vers les services web XML.
  • DCOM lorsque les composants gérés sont hébergés dans Dllhost. Cette option offre des performances et une sécurité optimales, ainsi que la possibilité de passer des contextes de service sur plusieurs ordinateurs. La principale question de conception lors du choix d’une technologie de communication à distance doit être de savoir si les services doivent ou non circuler entre les machines. Par instance, au sein d’une batterie de serveurs où une transaction est créée sur un ordinateur et qu’il est nécessaire que la transaction se poursuive sur un autre ordinateur, DCOM est le seul protocole qui peut être utilisé pour y parvenir. Toutefois, si les clients doivent simplement appeler un ServicedComponent distant, les approches du canal HTTP ou du point de terminaison SOAP sont de bonnes alternatives.
  • Un canal de communication à distance .NET (pour instance, un canal TCP ou personnalisé). Pour utiliser le canal TCP, vous avez besoin d’un processus à l’écoute sur un socket. En général, un processus personnalisé est utilisé pour écouter sur un socket, puis héberger des composants pris en charge en tant que bibliothèque COM+ ou application serveur. Vous pouvez également utiliser Dllhost comme écouteur. L’une ou l’autre approche est moins susceptible d’être utilisée et nécessite l’écriture d’un écouteur de socket personnalisé avec des performances, une scalabilité et une sécurité éprouvées. Par conséquent, les solutions ASP.NET ou DCOM sont les meilleures approches pour la plupart des projets.

Pour accéder à un composant géré à distance à l’aide de DCOM et hébergé dans Dllhost, vérifiez d’abord que l’assembly est inscrit dans une application serveur COM+ et placé dans le GAC sur l’ordinateur serveur. Ensuite, utilisez la fonctionnalité d’exportation d’application COM+ pour créer un fichier MSI pour un proxy d’application. Installez le proxy d’application sur le client. L’assembly managé est incorporé dans le proxy d’application. Le programme d’installation inscrit également l’assembly et le place dans le GAC sur l’ordinateur client. Par conséquent :

  • Le .NET Framework doit être installé sur le client et le serveur. Cette opération est obligatoire sur l’ordinateur client, même si seuls les clients non gérés accèdent aux composants gérés à distance. Sur les plateformes Windows 2000, Le Service Pack 3 est également requis.
  • Après avoir désinstallé le proxy, l’assembly doit également être supprimé du GAC.

L’infrastructure après l’activation du composant serveur dans le code managé côté client est illustrée dans la figure 6.

L’utilisation de DCOM implique que le CLR est hébergé dans Dllhost, ce qui signifie que le fichier de configuration de l’application dllhost.exe.config réside dans le répertoire system32. Cela implique également que le fichier de configuration s’applique à tous les processus Dllhost sur l’ordinateur. Dans la prochaine version du .NET Framework (V1.1), le répertoire racine de l’application COM+ peut être défini dans l’application COM+ et ce répertoire est utilisé pour découvrir les fichiers de configuration et les assemblys de l’application.

Pour les objets activés par le client, chaque fois que l’URI d’un objet est demandé, un bail de durée de vie est créé pour cet objet. Comme décrit précédemment dans la section Activation, un URI est demandé par le proxy de composant de service distant. Cela peut également se produire lorsqu’un composant serviceé in-process existant est marshalé dans un processus distant : un URI est demandé chaque fois qu’un objet MBR est marshalé par .NET en dehors d’un domaine d’application. L’URI est utilisé pour s’assurer que les identités d’objet dans .NET sont uniques et pour empêcher le chaînage de proxy. Par conséquent, lorsqu’un client managé active un composant de service distant, les durées de bail sont utilisées sur l’objet serveur. Notez que les clients non managés n’ont pas de proxy de composant de service distant côté client et ne demandent donc pas l’URI de l’objet. Au lieu de cela, les clients non managés utilisent DCOM pour garantir l’identité des objets. Par conséquent, les durées de bail sur les composants pris en charge ne sont pas utilisées lorsqu’ils sont activés à partir de clients non managés.

Lorsque les durées de bail sont impliquées dans des composants pris en charge, il est recommandé de définir les valeurs de délai d’expiration InitialLeaseTime et RenewOnCallTime sur une petite valeur, éventuellement aussi petite que 10 secondes. Les composants pris en charge sont détruits à l’aide de Dispose() ou lorsque le GC propre les objets. Lorsque Dispose() est appelé, le proxy de composant serviceé distant libère la référence qu’il a sur le proxy DCOM, puis se met à la disposition du gc suivant. L’objet serveur traite l’appel Dispose (ou un nouvel objet serveur est créé pour traiter l’appel distant à Dispose()), détruit le contexte COM+ associé, puis se met à la disposition du gc suivant, mais uniquement lorsque le délai de bail a expiré. Lorsque le client n’appelle pas Dispose(), le serveur doit d’abord attendre que le GC côté client libère la référence au proxy DCOM et qu’il ne se rende que le contexte COM+ à la disposition du gc suivant après l’expiration du bail. Par conséquent, appelez Dispose() et, en outre, réduisez le temps de bail par défaut. Même si le client reste actif et que le délai de bail expire, la référence DCOM à l’objet serveur maintient l’objet serveur actif. Toutefois, la référence DCOM n’est pas toujours utilisée pour maintenir le composant pris en service actif. Lorsque le client accède à l’objet via un canal de communication à distance CLR ou des services SOAP COM+, seule la référence forte due au bail maintient le composant pris en charge.

Conclusion

Cet article a abordé quelques-uns des services disponibles pour le code managé. Tous les services COM+ sont disponibles pour le code managé, tels que les niveaux d’isolation des transactions, l’initialisation des processus, les services sans composants et le recyclage des processus. Le .NET Framework fournit désormais un accès égal à tous les services COM+ de manière cohérente et logique. En outre, un certain nombre de composants innovants du .NET Framework, tels que ASP.NET, Microsoft ADO.NET et La messagerie, s’intègrent profondément aux services .NET Enterprise Services, en utilisant des services tels que les transactions et le regroupement d’objets. Cette intégration fournit une architecture et un modèle de programmation cohérents. L’espace de noms System.EnterpriseServices fournit le modèle de programmation pour ajouter des services aux classes managées.