Exporter (0) Imprimer
Développer tout

O'COM+

Rockford Lhotka
Magenic Technologies

Téléchargez le fichier exemple vbEnterprise.exe à partir de MSDN Code Center Lien non MSDN France Site en anglais.

Notez que les commentaires des programmeurs sont en anglais dans les fichiers du programme exemple mais sont traduits au sein de l'article dans un souci de clarté.

Le film "O'Brother" est une adaptation de l'Odyssée d'Homère destinée au public d'une époque nouvelle. De même, on peut dire qu'EnterpriseServices de .NET Framework est à peu de choses près une mise à jour de COM+ fonctionnant dans le nouvel environnement .NET.

Il est facile de déclarer qu'EnterpriseServices dans .NET nous donne accès à toutes les fonctionnalités de COM+. Bien que ce soit exact, c'est tout à fait insuffisant, car vous risqueriez de croire qu'il vous suffit de continuer à utiliser les services COM+ de .NET exactement comme ceux de COM, avec un simple changement de dénomination.

En réalité, certains des avantages extraordinairement positifs offerts par COM+ au développement COM ne le sont pas autant dans .NET. Dans d'autres situations, des fonctionnalités de COM+ dont ne bénéficiaient pas les développeurs en Visual Basic 6.0 sont entièrement accessibles lors du développement en Visual Basic .NET. Tout comme "O'Brother", le scénario sous-jacent est similaire, avec toutefois quelques différences par rapport à l'original.

Je dois également indiquer que COM+ n'est pas identique à Microsoft Transaction Server (MTS). MTS était une extension de Microsoft Windows NT® 4.0. COM+ fournit toutes les fonctionnalités de MTS et davantage, mais il est intégré directement à Windows® 2000 et Windows XP. Le code écrit dans .NET à l'aide d'EnterpriseServices peut être exécuté dans COM+, mais pas dans MTS.

Il serait faux de supposer que du fait que vous développez une application d'entreprise, vous devez utiliser EnterpriseServices. EnterpriseServices comprend une liste de services et de fonctionnalités spécifiques qui doivent être évalués individuellement pour vérifier s'ils sont pertinents pour votre application.

Ces services sont très puissants et permettent de simplifier le code de façon spectaculaire dans de nombreuses situations, mais ils ne doivent être utilisés que lorsque les fonctionnalités satisfont aux exigences spécifiques de votre application.

Dans cet article, j'aborderai ces différents services et fournirai quelques indications quant à leur utilité. Nous écrirons ensuite le code qui nous permettra d'exploiter la fonctionnalité la plus courante?la prise en charge transactionnelle à 2 phases.

Les principaux services d'EnterpriseServices sont décrits dans le tableau suivant :

Service ou fonctionnalité Description Commentaires sur l'utilisation
Protection transactionnelle à 2 phases Assure la protection transactionnelle des mises à jour de deux bases de données ou plus. Elle n'est pas nécessaire pour la mise à jour d'une seule base de données (ou de plusieurs tables d'une même base de données).

Nous permet de créer de nombreux composants discrets qui mettent à jour les données sans que nous ayons à nous soucier de la coordination des transactions entre les composants.

Met à notre disposition des "transactions automatiques".

N'utilisez cette fonctionnalité que si vous devez mettre à jour deux bases de données ou plus en mode transactionnel.

Utilisez cette fonctionnalité si vous créez des composants d'accès aux données réutilisables (bien que la réutilisation des composants d'accès aux données soit rare).

Utilisez cette fonctionnalité pour ne pas vous soucier de la gestion de vos propres transactions. Au lieu de vérifier manuellement que vous utilisez les mêmes objets Connection et Transaction, utilisez la protection transactionnelle d'EnterpriseServices pour simplifier votre code.

Évitez d'utiliser cette fonctionnalité si vous mettez à jour une seule base de données et ne créez pas de composants de données réutilisables susceptibles d'être appelés à partir d'autres configurations.

Activation juste-à-temps (JIT) Crée votre objet pour assurer l'appel d'une méthode spécifique, puis le détruit lorsque la méthode est terminée. N'utilisez cette fonctionnalité que si vous utilisez EnterpriseServices pour d'autres fonctionnalités.

Cette fonctionnalité est automatiquement disponible dans Web Services et .NET Remoting avec un objet Singlecall.

Loosely Coupled Events (Événements à configuration dispersée) Offre un modèle d'événement éditeur-abonné utilisant les événements COM. Utilisez cette fonctionnalité si les objets de l'abonné ne doivent pas s'exécuter en permanence et doivent être démarrés lors de la publication d'un événement.

Si l'éditeur et les abonnés s'exécutent en permanence, envisagez l'utilisation d'événements .NET et/ou de délégués pour obtenir cette fonctionnalité.

Chaîne Object Construction (Construction d'objet) Si votre composant nécessite une seule chaîne de configuration, elle peut être fournie par l'intermédiaire d'EnterpriseServices. N'utilisez cette fonctionnalité que si vous utilisez EnterpriseServices pour d'autres fonctionnalités.

En général, c'est le fichier de configuration de votre application qui est utilisé à cette fin.

Object Pooling (Pool d'objet) Si vous devez gérer un pool d'objets identiques, pour que le nombre d'objets ne soit jamais inférieur à un minimum ou supérieur à un maximum, cette fonctionnalité est faite pour vous. Utilisez cette fonctionnalité si vous avez besoin d'un ensemble préchargé d'objets identiques.

Utilisez cette fonctionnalité si vous voulez contrôler le nombre maximum d'objets exécutés à un moment donné, provoquant l'attente des clients jusqu'à la libération d'un objet.

Si votre pool ne doit contenir qu'un seul objet, envisagez l'option Singleton de .NET Remoting.

Queued Components (Composants en file d'attente) Cette fonctionnalité permet au code client d'appeler des méthodes sur un objet?tous ces appels de méthode étant enregistrés, mis en file d'attente, puis lus sur l'objet réel ultérieurement.

Les composants en file d'attente sont puissants et offrent automatiquement la gestion de nouvelle tentative de message et de message inactif, ainsi que des informations de sécurité et d'identité de l'appelant à l'objet.

Utilisez cette fonctionnalité pour insérer un comportement asynchrone dans votre application sans modifier de façon radicale la manière dont les clients utilisent vos objets.

Utilisez cette fonctionnalité si vous recherchez des fonctionnalités de gestion de nouvelle tentative de message et de message inactif.

Si vous ne recherchez que la mise en file d'attente des messages, envisagez l'espace de noms System.Messaging.

Role-Based Security (Sécurité basée sur les rôles) Vous permet de contrôler l'accès à vos composants en fonction de l'identité de l'appelant et de son rôle.

Notez que la sécurité basée sur les rôles du CLR ne traverse pas le canal TCP Remoting, et nécessite d'autres tâches pour traverser le canal HTTP.

Utilisez cette fonctionnalité si vous voulez exécuter des composants de couche intermédiaire sous un compte d'utilisateur particulier.

Utilisez cette fonctionnalité si vous voulez contrôler la sécurité de couche intermédiaire dans le cadre des tâches administratives.

Utilisez cette fonctionnalité si vous devez propager des informations de sécurité entre les composants de couche intermédiaire. .NET Framework contient de nombreuses fonctionnalités de sécurité qui peuvent être appliquées à Web Services, au Remoting et à d'autres composants de couche intermédiaire hors d'EnterpriseServices.

Services SOAP Vous permet d'exposer un composant en tant que service Web XML directement à partir de COM+ sur Windows XP.

Notez que cette fonctionnalité utilise le runtime .NET pour exposer l'application COM+ par l'intermédiaire de SOAP et assure l'accès efficace aux composants .NET s'exécutant dans COM+.

N'utilisez cette fonctionnalité que si vous faites appel à EnterpriseServices pour d'autres fonctionnalités et si vous souhaitez que votre composant soit disponible par l'intermédiaire de SOAP à partir de Windows XP.

Sous Windows 2000, vous pouvez utiliser la technique de Remoting illustrée plus loin dans cet article pour effectuer la même tâche.

Synchronisation Contrôle automatiquement la manière dont plusieurs threads accèdent à vos objets. Utilisez cette fonctionnalité si vous devez vous assurer que seule une thread qui fait partie de votre activité peut appeler des méthodes sur votre objet.

Cela n'est pas nécessaire dans la plupart des scénarios de Web Services, de Remoting ou d'activation JIT.

De toutes ces fonctionnalités, la plus utilisée et la plus connue est certainement la prise en charge transactionnelle à 2 phases. Malheureusement la partie à 2 phases est souvent laissée de côté, ce qui fait croire à beaucoup que cette technologie doit être utilisée pour obtenir la prise en charge transactionnelle. Elle est donc utilisée pour protéger en mode transactionnel l'accès à une seule base de données.

Cela doit être étudié avec soin. Certains coûts de performances associés à l'utilisation des transactions à 2 phases ne sont pas nécessaires lorsque vous utilisez une seule base de données. Néanmoins, les bénéfices des transactions automatiques en termes de simplicité et de souplesse de code sont également convaincants. Dans de nombreuses situations, la simplicité d'utilisation des transactions EnterpriseServices peut l'emporter sur l'impact des performances. Il est également bon de noter qu'un certain nombre de fonctionnalités souvent attribuées à COM+ existent sans avoir à utiliser EnterpriseServices. En particulier les fonctionnalités décrites ci-dessous.

  • Regroupement de connexion à la base de données : fourni par les fournisseurs managés ODBC, OleDb et ADO.NET. Notons que le fournisseur managé .NET pour SQL Server dispose d'un algorithme de regroupement de connexion hautement optimisé et qu'il est beaucoup plus rapide que celui utilisé par OleDb ou ODBC.
  • Gestion des threads : fournie par IIS et ASP.NET pour Web Services, et par le sous-système .NET Remoting pour les serveurs d'accès distant.
  • Transactions simples : ADO.NET ou les procédures stockées peuvent apporter toute la protection transactionnelle dont vous avez besoin pour mettre à jour plusieurs tables d'une même base de données.
  • Messages en file d'attente : System.Messaging donne accès aux fonctionnalités de MSMQ. Cela est différent des Queued Components qui utilisent également MSMQ, mais à un niveau plus abstrait, pour offrir non seulement la file d'attente de messagerie, mais également la gestion de nouvelle tentative de message et de message inactif.

Vous pouvez utiliser ces informations pour mieux décider à quel moment vous devez ou non utiliser EnterpriseServices dans votre application.

Cependant, il y a fort à parier que si vous créez une application d'entreprise vous finirez par avoir besoin de mettre à jour plusieurs bases de données en mode transactionnel. À cette fin, parcourons le code nécessaire pour utiliser les fonctionnalités d'EnterpriseServices?en particulier les fonctionnalités transactionnelles à 2 phases.

Utilisation d'EnterpriseServices

Si nous écrivons un assembly .NET utilisant EnterpriseServices, il finira par s'exécuter dans une application COM+. Pour que cela se produise, notre code .NET doit comporter un certain nombre de caractéristiques et de fonctionnalités supplémentaires. Après tout, COM+ est une technologie COM, c'est pourquoi notre assembly a besoin d'un certain niveau d'interopérabilité COM pour fonctionner dans cet environnement. Par ailleurs, nous aurons besoin d'accéder à l'objet de contexte COM+ pour pouvoir interagir avec l'environnement.

Référence à System.EnterpriseServices.dll

Pour créer un assembly qui utilise les services d'entreprise et interagisse correctement avec COM+, nous devons ajouter une référence à System.EnterpriseServices.dll dans notre projet.

Ouvrez Visual Studio® .NET, puis créez un projet de Bibliothèque de classes nommé PubsServices. Utilisez ensuite la boîte de dialogue Ajouter une référence pour ajouter une référence à la DLL comme le montre la figure 1.

Ajout d'une référence à System.EnterpriseServices.dll
Figure 1. Ajout d'une référence à System.EnterpriseServices.dll

Attributs de l'assembly

Les assemblys .NET sont placés dans COM+ automatiquement (lorsqu'une application cliente .NET sur le même ordinateur appelle l'assembly) ou en utilisant l'utilitaire de ligne de commande regsvcs.exe. Dans les deux cas, il est nécessaire de fournir certaines informations pour que l'assembly puisse être chargé correctement dans une application COM+.

Les projets Visual Basic® .NET démarrent toujours par un fichier AssemblyInfo.vb contenant certains paramètres concernant l'assembly. Lors de l'utilisation d'EnterpriseServices, il est nécessaire d'ajouter quelques lignes de code supplémentaires dans ce fichier.

En haut du fichier, importez l'espace de noms :

Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.EnterpriseServices

Ajoutez ensuite les lignes de code suivantes dans le fichier :

' Attributs des services d'entreprise
<Assembly: ApplicationName("PubsServices")> 
<Assembly: Description("Pubs data access services")> 
<Assembly: ApplicationActivation(ActivationOption.Library)>

L'attribut ApplicationName indique le nom de l'application COM+ dans laquelle notre assembly doit être placé. Si cette application existe, l'assembly y est ajouté ; dans le cas contraire, l'application est créée automatiquement pour nous.

L'attribut Description fournit simplement une description explicite de notre assembly.

L'attribut ApplicationActivation ne sert que lorsqu'une nouvelle application COM+ doit être créée pour que notre assembly soit installé dans COM+. Lorsqu'une application est créée, cet attribut contrôle si elle est créée comme une application Bibliothèque ou Serveur. Si l'application existe déjà dans COM+, son paramètre actuel n'est pas modifié par cette valeur.

Si vous voulez utiliser la fonctionnalité des composants en file d'attente, vous devez également ajouter la ligne :

<Assembly: ApplicationQueuing()>

D'autres fonctionnalités d'EnterpriseServices peuvent également avoir des attributs au niveau de l'assembly. Consultez l'aide du kit de développement .NET Framework pour en savoir plus sur des fonctionnalités particulières et leurs attributs.

Nom fort

Il existe une autre exigence pour les composants qui s'exécutent dans COM+. L'assembly doit être pourvu d'un nom fort permettant au runtime .NET de l'identifier de façon unique. Un nom fort est constitué du nom de l'assembly, du numéro de version et de la clé unique de l'éditeur.

Il se peut que vous ou votre entreprise ayez déjà une clé de l'éditeur, auquel cas vous devez l'utiliser. Dans le cas contraire, vous pouvez en créer une en utilisant l'utilitaire de ligne de commande sn.exe avec une commande telle que :

sn ?k mykey.snk

Le fichier obtenu contient votre clé que vous pouvez utiliser pour donner un nom fort aux assemblys. Votre entreprise doit disposer d'une clé pour que les assemblys que vous créez puissent être signés par la même clé d'éditeur.

Pour donner un nom fort à un assembly, insérez le code suivant dans le fichier AssemblyInfo :

' Nom fort
<Assembly: AssemblyKeyFile("mykey.snk")>

Lorsque l'assembly est compilé, la partie publique de la clé est automatiquement incorporée dans la DLL, pour lui donner un nom fort et le rendre utilisable par EnterpriseServices.

Configuration de l'application COM+

Il arrive parfois que les attributs ajoutés à AssemblyInfo ne contiennent pas suffisamment d'informations pour configurer complètement une application COM+. EnterpriseServices nous offre un accès par programmation aux seuls attributs qui concernent un développeur. Les autres attributs, tels que le compte sous lequel une application serveur doit s'exécuter, sont définis par un administrateur système lors de l'installation du logiciel sur le serveur.

Par exemple, lors de la création d'une application serveur, l'application est configurée pour s'exécuter comme utilisateur interactif, ce qui signifie qu'elle utilise l'identité de l'utilisateur actuellement connecté par défaut. Cela n'est pas idéal, car il peut en découler des résultats imprévisibles et non une solution sûre, du fait que nos composants sont appelés lorsque des individus différents sont connectés à l'ordinateur. Ils peuvent également échouer lorsque personne n'est connecté au serveur.

Il est donc important de créer manuellement l'application par avance ou d'y entrer après la création automatique de l'application par .NET et de changer le paramètre afin de privilégier un utilisateur particulier. Cela s'effectue en utilisant la console Services de composants sous Outils d'administration et en définissant les propriétés de l'Application. La figure 2 illustre l'onglet Identité renseigné par un utilisateur particulier plutôt que par la valeur par défaut :

Fig. 2
Figure 2. Exécutez l'application sous un nom d'utilisateur particulier

Les autres détails que nous pouvons configurer incluent le nombre de minutes pendant lesquelles l'application continue de s'exécuter après la dernière utilisation et les autorisations accordées pour la suppression, la modification et le débogage. Ces options se trouvent dans l'onglet Avancé de la boîte de dialogue Propriétés.

Sous-classement de ServicedComponent

Toute classe qui utilisera les fonctionnalités d'EnterpriseServices ou y participera doit être une sous-classe de System.EnterpriseServices.ServicedComponent. Modifiez le code dans Class1.vb de la façon suivante :

Imports System.EnterpriseServices

Public Class AuthorService
 Inherits ServicedComponent

End Class

En héritant de ServicedComponent, cette classe est à même d'utiliser EnterpriseServices. Les services spécifiques qu'elle utilisera sont déterminés par les attributs que nous plaçons sur la classe, sur les méthodes de la classe, et sur le code que nous écrivons dans ces méthodes.

Attributs de la classe

La plupart des services que nous utiliserons nécessitent l'ajout d'un ou plusieurs attributs à la classe. Il existe également certains attributs qui s'appliquent à tous les services.

Le plus couramment appliqué est <EventTrackingEnabled()>. Cet attribut permet la surveillance de ce composant afin que la console de gestion Services de composants puisse afficher le statut de notre composant et montrer combien il existe d'objets et combien sont actifs :


<EventTrackingEnabled(True)> _
Public Class AuthorService

D'autres exemples incluent l'activation du pool d'objet, dans lequel nous avons ajouté :


<ObjectPooling(True, 10, 50)> _
Public Class AuthorService

Ou pour activer la synchronisation nous ajouterions :


<Synchronization(SynchronizationOption.Required)> _
Public Class AuthorService

Chaque fonction spécifique d'EnterpriseServices possède son propre ensemble d'attributs qui doivent être appliqués aux classes que nous écrivons pour utiliser ou implémenter cette fonctionnalité. L'aide du kit de développement .NET Framework contient des détails et des exemples concernant ces fonctionnalités.

Attributs de méthode

De nombreux services disposent également d'attributs associés qui peuvent être appliqués aux méthodes pour mieux en contrôler le comportement. Par exemple, nous pouvons sécuriser une méthode utilisant la sécurité basée sur les rôles en appliquant l'attribut qui suit :


<SecurityRole("Admin")> _
Public Sub DoWork()

Comme avec les attributs de classe, chaque fonctionnalité spécifique d'EnterpriseServices a son propre ensemble d'attributs pour les méthodes. Consultez l'aide du kit de développement .NET Framework pour obtenir des détails et des exemples concernant ces fonctionnalités.

Dans cet exemple, nous allons utiliser la fonctionnalité la plus courante?la prise en charge transactionnelle à 2 phases?et montrer comment créer un composant transactionnel.

Prise en charge transactionnelle à 2 phases

De nombreuses applications d'entreprise interagissent simultanément avec plusieurs bases de données. Il arrive parfois que nous devions mettre à jour plusieurs bases de données dans un paramètre transactionnel, ce qui s'avère une tâche très complexe. La technique qui gère cela se nomme validation à 2 phases, dans laquelle les mises à jour sont effectuées dans chaque base de données, mais ne sont validées que lorsque toutes les bases de données confirment qu'elles sont prêtes.

Heureusement, toute la complexité nécessaire à l'implémentation de la validation à 2 phases est gérée par le Distributed Transaction Coordinator (DTC), qui à son tour est géré pour nous par COM+. Dans .NET, nous n'interagissons même pas directement avec COM+, et laissons EnterpriseServices gérer la plupart des détails.

Comme les autres services, les services transactionnels nécessitent que notre classe hérite de ServicedComponent et inclue un attribut indiquant nos exigences transactionnelles. Modifiez le code dans Class1.vb de la façon suivante :

Imports System.EnterpriseServices

<Transaction(TransactionOption.Required), _
EventTrackingEnabled(True)> _
Public Class AuthorService
 Inherits ServicedComponent

End Class

L'attribut Transaction nous permet d'indiquer les exigences de transaction de notre objet en fonction des options décrites ci-dessous :

  • Disabled : l'objet s'exécutera dans le même contexte avec les autres composants transactionnels, sans être lui-même transactionnel.
  • NotSupported : l'objet s'exécutera dans un contexte distinct des autres composants transactionnels et ne sera pas lui-même transactionnel.
  • Required : l'objet participera à une transaction existante, ou une transaction sera créée si aucune n'existe.
  • RequiresNew : l'objet s'exécutera toujours dans une nouvelle transaction, même s'il en existe déjà une.
  • Supported : l'objet participera à une transaction existante, ou s'exécutera sans transaction si aucune n'existe.

Toutes les méthodes de cette classe seront protégées en mode transactionnel. En d'autres termes, les accès aux données exécutés seront gérés par le DTC et subiront les inconvénients et les avantages du traitement de validation à 2 phases.

Les méthodes proprement dites peuvent également être pourvues d'un attribut <AutoComplete()> pour contrôler dans quelle mesure notre code interagit avec COM+ pour gérer la transaction.

Si l'attribut autocomplete a la valeur true, le succès ou l'échec de notre méthode est décidé selon que la méthode provoque ou non une erreur. Si une erreur est provoquée, la transaction est restaurée dans sa totalité. Si aucune erreur n'est provoquée, nous votons pour valider la transaction qui est validée en supposant que d'autres composants n'échouent pas.

Si l'attribut autocomplete a la valeur false, nous choisissons de contrôler directement le vote de succès/échec de notre méthode. En d'autres termes, nous devons écrire le code dans notre méthode pour détecter si nous avons réussi et pour voter suivant les cas.

Utilisation de l'attribut AutoComplete

Dans la plupart des cas, nous utiliserons autocomplete, car il constitue l'approche de codage la plus simple. Par exemple, nous pouvons écrire une méthode pour mettre à jour le nom d'un auteur en ajoutant le code suivant à la classe :

Private DB1 As String = _
  ConfigurationSettings.AppSettings("DB1")

 <AutoComplete(True)> _
 Public Sub UpdateName(ByVal au_id As String, ByVal lname As String)
  Dim cn As New SqlConnection(DB1)
  cn.Open()
  Dim cm As New SqlCommand()

  Try
   cm.CommandType = CommandType.Text
   cm.Connection = cn
   cm.CommandText = "UPDATE authors SET au_lname='" & lname & _
    "' WHERE au_id='" & au_id & "'"
   cm.ExecuteNonQuery()
  Finally
   cm.Dispose()
   cn.Close()
   cn.Dispose()
  End Try
 End Sub

Cette procédure nécessite également l'importation des espaces de noms System.Data.SqlClient et System.Configuration en haut du fichier.

Il s'agit de code ADO.NET standard pour ouvrir une connexion à la base de données Pubs sur un serveur et pour simplement mettre à jour le nom d'un auteur particulier. La plus grande partie du code se résume à un bloc Try..Finally, pour nous assurer de fermer la connexion à la base de données qu'il y ait une erreur ou non.

Remarquez l'attribut <AutoComplete(True)>, qui indique que cette méthode ne vote pour la restauration de la transaction que dans le cas où une erreur est provoquée. Puisque notre code ne récupère aucune erreur, si une erreur est détectée, elle est provoquée et la transaction est restaurée dans sa totalité. Si nous ne rencontrons aucune erreur, ce code vote pour valider la transaction.

Transactions manuelles

Autocomplete est l'approche la plus simple pour écrire du code protégé en mode transactionnel en utilisant EnterpriseServices. Nous pouvons modifier le code pour effectuer le vote manuellement de la façon suivante :


<AutoComplete(False)> _
 Public Sub UpdateName(ByVal au_id As String, ByVal lname As String)
  Dim cn As New SqlConnection(DB1)
  cn.Open()
  Dim cm As New SqlCommand()

  Try
   cm.CommandType = CommandType.Text
   cm.Connection = cn
   cm.CommandText = "UPDATE authors SET au_lname='" & lname & _
    "' WHERE au_id='" & au_id & "'"
   cm.ExecuteNonQuery()
   ContextUtil.SetComplete()
 Catch
  ContextUtil.SetAbort()
  Finally
   cm.Dispose()
   cn.Close()
   cn.Dispose()
  End Try
 End Sub

À présent, nous avons <AutoComplete(False)> qui indique que nous voulons gérer manuellement les détails. Cela signifie que nous devons maintenant appeler explicitement SetComplete ou SetAbort pour indiquer respectivement le succès ou l'échec.

Ces deux méthodes viennent de l'objet ContextUtil. L'objet ContextUtil est disponible dans notre code, car nous disposons d'une sous-classe : ServicedComponent. Cet objet nous donne accès au même objet de contexte que celui que nous avons utilisé dans Visual Basic 6.0, et nous permet de voter manuellement pour valider la transaction ou la restaurer.

À cette fin, nous avons ajouté un bloc Catch à la gestion des erreurs pour pouvoir détecter si une erreur s'est produite et appeler SetAbort ; sinon, nous appelons SetComplete.

Dans ce cas, nous n'avons rien gagné par rapport à l'utilisation d'autocomplete. En fait, nous avons dû écrire plus de code, ce qui est un inconvénient. Généralement autocomplete est la meilleure façon de procéder, bien qu'il existe des cas où nous pouvons souhaiter voter pour une restauration sans provoquer d'erreur, auquel cas l'approche manuelle est préférable.

Utilisation de plusieurs bases de données

Jusque-là, il n'y a aucune raison d'utiliser des transactions à 2 phases, car nous ne mettons à jour qu'une seule base de données. Modifions le code pour mettre à jour deux bases de données et mieux le comprendre.

Pour ce faire, il existe bien entendu plusieurs procédures. Nous pouvons créer plusieurs assemblys?un par base de données. Nous pouvons créer plusieurs classes dans le même assembly?une par base de données. Nous pouvons créer plusieurs méthodes dans la même classe?une par base de données. Dans la mesure où tous ces scénarios sont totalement pris en charge, comment savoir lequel utiliser suivant la situation ?

La création de plusieurs assemblys est une bonne technique si nous essayons de subdiviser notre application parallèlement à la base de données ou au secteur d'activité. Ce choix permet de réutiliser certains des composants hors du scénario que nous créons ici. Dans ce cas, nous mettrons à jour deux tables d'auteurs. Si nous pensons mettre à jour parfois l'une sans l'autre, mais dans le cadre d'une autre transaction, la création de plusieurs assemblys peut être pertinente.

Dans la réalité, ce type de réutilisation est très rare. Bien que les objets d'accès aux données de nombreuses applications soient subdivisés en de nombreux composants, le niveau de réutilisation est très faible. La multiplicité des composants se traduisant par une complexité accrue, nous introduisons une complexité supplémentaire pour peu ou pas de réutilisation dans la plupart des cas.

La création de plusieurs objets dans le même assembly est assez comparable. La raison motivant la création de plusieurs objets est que nous pouvons ainsi exposer chaque objet séparément. Dans le futur nous pouvons ainsi en appeler un sans appeler l'autre. Ce type de réutilisation est également rare.

En fait, dans de nombreux cas, appeler un composant sans l'autre n'a aucun sens, c'est la raison pour laquelle nous les avons regroupés au départ en mode transactionnel. Par exemple, il est absurde d'enregistrer un élément d'une ligne de facture sans enregistrer l'en-tête de la facture. La division en objets distincts pour pouvoir les appeler séparément augmente les risques de mauvaise utilisation et crée des problèmes de maintenance à long terme.

En d'autres termes, dans la plupart des cas il est préférable d'exposer simplement une seule méthode Public à partir d'un seul objet que le client peut appeler. Cette méthode peut ensuite appeler les méthodes Private appropriées qui permettent de réaliser les mises à jour de données du scénario. Cela conserve la simplicité de l'interface publique et rend difficile sa mauvaise utilisation. La réduction de la complexité et des risques de mauvaise utilisation se traduit par une simplicité de gestion à long terme. Bien entendu, nous envisageons la possibilité de réutiliser une partie du code ; c'est pourquoi vous devez évaluer votre choix en fonction de votre application spécifique.

Dans ce cas, nous allons implémenter deux méthodes Private pour mettre à jour les bases de données, et une méthode Public qui peut être appelée par le client. La méthode Public utilise autocomplete pour gérer la transaction :

<AutoComplete(True)> _
 Public Sub UpdateName(ByVal au_id As String, ByVal lname As String) 
  UpdateDB1(au_id, lname) 
  UpdateDB2(au_id, lname) 
 End Sub

Souvenez-vous que toute erreur provoquera le vote en faveur d'une restauration. Tant que nous écrivons les méthodes UpdateDB1 et UpdateDB2 pour provoquer des erreurs en cas d'échec, c'est la transaction entière qui échouera en cas d'erreur quelle qu'elle soit.

La méthode UpdateDB1 est le code que nous avons écrit précédemment, placé dans une méthode Private, sans être paré d'aucun attribut spécial :

Private Sub UpdateDB1(ByVal au_id As String, ByVal lname As String)
  Dim cn As New SqlConnection(DB1)
  cn.Open()
  Dim cm As New SqlCommand()

  Try
   cm.CommandType = CommandType.Text
   cm.Connection = cn
   cm.CommandText = "UPDATE authors SET au_lname='" & lname & _
    "' WHERE au_id='" & au_id & "'"
   cm.ExecuteNonQuery()
  Finally
   cm.Dispose()
   cn.Close()
   cn.Dispose()
  End Try
 End Sub

Cette méthode n'a pas besoin d'un attribut pour contrôler la transaction, car elle sera appelée par la méthode UpdateName, qui a cet attribut. La transaction est simplement gérée à un niveau supérieur.

Cela vaut également pour UpdateDB2. En premier lieu, nous avons besoin d'une autre constante pour la chaîne de connexion à la base de données :

Private Const DB2 As String = _
   ConfigurationSettings.AppSettings("DB2")

Cette chaîne de connexion se réfère à l'instance SQL Server ajoutée par le kit de développement .NET. La plupart des installations .NET ont à la fois une instance SQL Server ordinaire et cette seconde instance, ce qui facilite le test des transactions à 2 phases.

Nous pouvons à présent écrire UpdateDB2 de la façon suivante :

Private Sub UpdateDB2(ByVal au_id As String, ByVal lname As String)
  Dim cn As New SqlConnection(DB2)
  cn.Open()
  Dim cm As New SqlCommand()

  Try
   cm.CommandType = CommandType.Text
   cm.Connection = cn
   cm.CommandText = "UPDATE authors SET au_lname='" & lname & _
    "' WHERE au_id='" & au_id & "'"
   cm.ExecuteNonQuery()
  Finally
   cm.Dispose()
   cn.Close()
   cn.Dispose()
  End Try
 End Sub

C'est le même code, simplement appliqué à une seconde base de données. Une fois encore, il n'implémente aucune protection transactionnelle directe qui est automatiquement gérée par l'attribut <AutoComplete()> sur la méthode UpdateName. Il nous suffit de nous assurer que tout échec de ce code provoquera une erreur, ce qui signifie simplement que nous ne devons pas intercepter d'erreurs dans ce code.

Nous avons maintenant terminé. Nous disposons d'un assembly qui s'exécutera dans une application COM+ et mettra à jour en mode transactionnel deux bases de données à la demande.

Utilisation du composant

Bien entendu, l'écriture de l'assembly ne constitue que la moitié de la tâche. Nous devons encore nous demander comment nous voulons l'appeler à partir de notre code client.

Si nous pouvons techniquement utiliser DCOM pour appeler l'assembly, cela suppose que nous exposions nos objets .NET par l'intermédiaire de COM interop, ce qui n'est pas idéal sauf si nous tentons de les appeler à partir de clients COM, tels que Visual Basic 6.0 ou ASP.

Avec les applications clientes .NET, il est possible d'utiliser des technologies .NET plus natives pour communiquer avec le composant. Cela nous laisse trois options principales :

  • Appel direct à partir du même ordinateur
  • Web Services
  • .NET Remoting

Ces trois options impliquent que du code soit exécuté sur le même serveur que le composant exécuté dans COM+ pour réaliser l'appel réel au composant.

Dans le premier cas, nous avons une application cliente exécutée sur le même ordinateur, ce qui signifie qu'elle peut simplement utiliser EnterpriseServices pour appeler le composant directement.

Nous pouvons construire un service Web XML qui permet aux clients d'autres ordinateurs d'utiliser XML et SOAP pour appeler le composant. Cela n'expose pas directement le composant. Nous construisons à la place un service Web qui prend l'appel du client et le délègue au composant exécuté dans COM+.

.NET Remoting permet à un client distant d'appeler directement notre composant un peu comme DCOM fonctionnait dans Visual Basic 6.0. Nous pouvons construire une application hôte Remoting qui expose directement le composant aux clients, et leur permettre d'interagir sur le réseau avec l'objet lorsqu'il est exécuté sur notre serveur dans COM+.

Appel direct

Pour commencer, examinons le cas le plus simple. Nous allons créer une application cliente qui référence directement et appelle PubsServices. Le client doit être exécuté sur le même ordinateur que le composant du serveur. Généralement, cela ne se produit pas pour un client Windows Forms, mais un client Web Forms peut en avoir besoin, car l'application ASP.NET peut être exécutée sur le même serveur que PubsServices.

Ajoutez une nouvelle application Web ASP.NET nommée PubsEntWeb à la solution.

Elle aura besoin d'une référence au projet PubsServices. C'est pourquoi vous devez utiliser l'onglet Projets de la boîte de dialogue Ajouter une référence pour ajouter une référence à ce projet.

Ajoutez ensuite deux étiquettes, deux zones de texte et un bouton à la page, comme dans la figure 3.

Fig. 3
Figure 3. Exemple de PubsEntWeb

Nommez les zones de texte respectivement txtID et txtName, puis le bouton btnSave. Notez la valeur du texte dans le contrôle txtID. Il s'agit de la valeur par défaut qui pointe vers l'un des éléments de la table des auteurs pour faciliter le test.

À présent, double-cliquez sur le bouton, puis ajoutez le code suivant pour interagir avec le composant PubsServices :

Private Sub btnSave_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles btnSave.Click

  Dim svc As New PubsServices.AuthorService()
  svc.UpdateName(txtID.Text, txtName.Text)
 End Sub

Il n'y a rien de spécial à cet endroit, nous créons simplement un objet et appelons une méthode. EnterpriseServices gère l'ensemble des détails à notre place : il reconnaît que l'assembly est exécuté dans COM+ et l'utilise correctement.

Souvenez-vous que nous avons préalablement utilisé System.Configuration pour obtenir les chaînes de connexion à la base de données. Ajoutez la section suivante à web.config pour les obtenir :

 <appSettings>
   <add key="DB1" 
value="Data Source=ineroth;Initial Catalog=pubs;User ID=sa;Password= " />
   <add key="DB2" 
value="Data Source=ineroth\NETSDK;Initial Catalog=pubs;User ID=sa;Password=" />
  </appSettings>

Générez la solution pour créer la DLL PubsServices et l'application Web Forms.

Inscription de l'assembly dans COM+

Comme il s'agit d'un client .NET, il tentera automatiquement de créer l'application COM+ et d'inscrire l'assembly dans cette application. Toutefois, la sécurité de Windows intervient ici. ASP.NET s'exécute sous un compte fortement contrôlé, qui ne dispose pas des privilèges requis pour réaliser l'installation.

Cela signifie que nous devons inscrire manuellement l'assembly avant de pouvoir exécuter l'application. Cela s'effectue en utilisant l'utilitaire de ligne de commande regsvcs.exe. Le moyen le plus simple d'accéder à cet utilitaire est d'ouvrir une invite de commande à l'aide de l'icône Invite de commandes Visual Studio .NET du menu Démarrer. Elle est située sous Microsoft Visual Studio .NET | Outils Visual Studio .NET.

Ouvrez le répertoire bin à la racine virtuelle de l'application Web Forms, puis entrez la commande :

regsvcs pubsservices.dll

Cette procédure inscrit la DLL dans COM+, en créant une application basée sur les informations que nous avons placées dans le fichier AssemblyInfo. Il est bon de noter que cette opération peut également s'effectuer par programmation en utilisant la classe System.EnterpriseServices.RegistrationHelper.

Souvenez-vous que vous devez revenir dans l'application COM+ et changer les informations de l'onglet Identité pour vous assurer que l'application s'exécute sous le compte d'utilisateur correct.

À présent, nous pouvons exécuter l'application. Nous devons voir que les deux tables sont mises à jour dans les deux bases de données, toutes sous la protection transactionnelle à 2 phases. Vous pouvez consulter l'état du composant dans la console de gestion Services de composants, en particulier pour constater qu'une instance de l'objet est active.

Fig. 4
Figure 4. Console de gestion Services de composants affichant l'état du composant (cliquez sur l'image pour l'agrandir)

Dans de nombreuses situations, il ne suffit pas que les composants COM+ soient exécutés sur le même ordinateur que le client. Pour les applications Web, nous avons souvent des impératifs de sécurité qui empêchent le serveur Web d'accéder directement à la base de données. Pour les applications Windows, il existe des bénéfices substantiels en terme d'évolutivité en faisant exécuter les composants d'accès aux données sur un serveur d'application central. Par ailleurs, il est généralement peu pratique d'exécuter COM+ sur chaque station de travail cliente.

Si nous voulons appeler le composant serveur à distance sur le réseau, nous disposons de trois options principales :

  • Web Services
  • .NET Remoting
  • DCOM

L'option DCOM n'est pas différente de ce que nous connaissions dans Visual Basic 6.0. Nous exportons un proxy d'application COM+ à partir de COM+ et l'installons sur l'ordinateur client. Nous copions ensuite l'application .NET sur le client et le composant distant sera appelé par l'intermédiaire de DCOM. Dans ce cas, le code est identique à l'exemple d'accès direct de cet article, si ce n'est le déploiement du proxy d'application COM+.

Parcourons les deux nouvelles approches en utilisant Web Services et .NET Remoting.

Web Services

Web Services permet d'appeler notre assembly sur le réseau. Deux approches sont possibles. La première consiste à créer un service Web ASP.NET qui délègue tous les appels de méthode à PubsServices à l'arrière-plan. La seconde ne fonctionne que sur Windows XP, mais nous permet d'exposer directement notre assembly par l'intermédiaire de SOAP en utilisant une option COM+.

Commencez par créer un projet de service Web conventionnel et par exposer notre composant de cette manière.

Ajoutez un projet de service Web ASP.NET à la solution et nommez-le PubsEnterprise.

À l'aide de la boîte de dialogue Ajouter une référence, ajoutez une référence au projet PubsServices, puis ouvrez la fenêtre de code pour Service1.asmx. Ajoutez le code suivant pour créer une méthode Web qui donne accès à notre composant serveur :

<WebMethod()> _
 Public Sub UpdateName(ByVal au_id As String, ByVal lname As String)
  Dim svc As New PubsServices.AuthorService()
  svc.UpdateName(au_id, lname)
 End Sub

Le client appellera notre service Web, qui à son tour va créer une instance du composant AuthorService dans COM+ et appeler sa méthode UpdateName. Bien que le client n'appelle pas le service COM+ directement, cela ne rend pas le service disponible par l'intermédiaire de la technologie de Web Services.

Appel du service Web

Le projet PubsEntWeb peut être mis à jour pour utiliser ce nouveau service Web. Utilisez la boîte de dialogue Ajouter une référence Web pour ajouter une référence au service comme le montre la figure 5.

Fig. 5
Figure 5. Ajout de la référence Web (cliquez sur l'image pour l'agrandir)

Nous pouvons à présent mettre à jour le code pour que notre page utilise le service Web et mette à jour le nom au lieu d'appeler directement PubsServices :

Private Sub btnSave_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles btnSave.Click

  Dim svc As New localhost.Service1()
  svc.UpdateName(txtID.Text, txtName.Text)
 End Sub

Puisque le nom de la méthode et les paramètres du service Web sont les mêmes que dans l'objet AuthorService proprement dit, le seul changement nécessaire consiste à créer une instance de l'objet de service Web plutôt que de l'objet AuthorService.

À présent, notre application Web s'exécute comme précédemment, bien qu'elle appelle maintenant le composant COM+ par l'intermédiaire d'un service Web. Cette procédure réduit considérablement les performances, car nous avons inséré un saut réseau entre l'application Web et le composant serveur, mais cela ne signifie pas que nous devions franchir un pare-feu interne pour améliorer la sécurité de notre application.

Remoting

.NET Remoting est le moyen le plus simple et le plus puissant de mettre notre composant serveur à la disposition des clients distants. Cette technique nous permet d'exposer directement l'assembly aux applications clientes sans avoir à créer manuellement un wrapper de service Web.

La technique illustrée ici est essentiellement la même que sur Windows XP lorsque nous activons la case à cocher pour exposer une application COM+ par l'intermédiaire de SOAP. Cependant, cette fonctionnalité n'est disponible que sur Windows XP. C'est pourquoi la technique que nous allons voir est importante, car elle nous permet d'exposer directement notre assembly aux clients.

Nous pouvons héberger des objets par l'intermédiaire de Remoting de deux manières. Nous pouvons créer une application hôte personnalisée, généralement un service Windows, ou utiliser un projet ASP.NET pour héberger nos objets. J'ai présenté l'option de service Windows dans des articles précédents ; dans cet article, j'utiliserai donc l'option ASP.NET.

Ajoutez un Projet Web vide à la solution et nommez-le PubsEntRemote. Cette procédure provoque la création et la configuration d'une racine virtuelle vide par Visual Studio .NET.

Utilisez ensuite la boîte de dialogue Ajouter une référence pour ajouter une référence au projet PubsServices. Cette procédure copie PubsServices.dll dans le répertoire bin de l'application, ce qui est nécessaire si nous voulons exposer les fonctionnalités par l'intermédiaire de Remoting.

Pour finir, utilisez l'option Projet | Ajouter un nouvel élément pour ajouter au projet un fichier de configuration Web nommé Web.config. Dans ce fichier, nous allons ajouter du code XML qui configure Remoting pour exposer notre composant aux applications clientes. Ajoutez le code suivant au fichier Web.config :

<configuration>
 <system.runtime.remoting>
  <application>
   <service>
    <wellknown 
     mode="SingleCall" 
     objectUri="AuthorService.rem"
     type="PubsServices.AuthorService, PubsServices" />
    </service>
   </application>
  </system.runtime.remoting>  

Notez que cette section n'est pas contenue dans l'élément <system.web>, mais se trouve à un niveau égal.

L'élément principal de cette section est <service>, dans lequel nous définissons la classe spécifique qui doit être exposée aux clients. Dans ce cas, nous spécifions que la classe AuthorService, contenue dans l'assembly PubsServices, doit être exposée en utilisant l'URI d'AuthorService.rem. Elle est configurée en mode SingleCall, ce qui signifie que chaque appel de méthode d'un client obtiendra une nouvelle instance de l'objet pour répondre à la requête. C'est le même comportement avec Web Services.

Combiné avec le nom du serveur et la racine virtuelle, nous avons simplement défini une URL par laquelle notre composant est accessible par l'intermédiaire de Remoting :

http://myserver/PubsEntRemote/AuthorService.rem

Appel du serveur distant

Nous pouvons à présent mettre à jour l'application cliente pour utiliser Remoting au lieu de Web Services. Commencez par reprendre le code qui se trouve derrière notre formulaire Web Form et changez-le pour qu'il n'utilise plus le service Web :

Private Sub btnSave_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles btnSave.Click

  Dim svc As New PubsServices.AuthorService()
  svc.UpdateName(txtID.Text, txtName.Text)
 End Sub

C'est le même code que celui avec lequel nous avons démarré, dans lequel l'objet AuthorService est simplement créé et utilisé. Là réside l'intérêt de Remoting?le code permettant d'utiliser un objet distant est identique à celui utilisé pour converser directement avec un objet. Nous disposons d'une totale transparence d'emplacement.

Bien entendu, nous devons avertir l'application cliente que l'objet AuthorService doit être appelé à distance et non localement. Cette procédure peut être effectuée par l'intermédiaire du fichier Web.config ou par programmation en mettant à jour le fichier Global.asax. Ouvrez la fenêtre de code du fichier Global.asax et modifiez la méthode Application_Start de la façon suivante :

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
  ' Se déclenche lors du démarrage de l'application
  RemotingConfiguration.RegisterWellKnownClientType( _
  GetType(PubsServices.AuthorService), _
  "http://localhost/pubsentremote/authorservice.rem")
 End Sub

Avec cette modification, lorsque l'application ASP.NET démarre, elle configure le sous-système Remoting pour qu'il soit averti que l'objet AuthorService doit être appelé sur un serveur distant. À cet endroit, le code de notre page ou de n'importe quelle autre partie de notre application interagit automatiquement avec l'objet distant plutôt que de tenter de l'utiliser localement.

Si nous exécutons maintenant l'application, elle fonctionne désormais comme précédemment, mais en utilisant Remoting pour appeler l'objet AuthorService. Comme avec Web Services, cette procédure permet de franchir un pare-feu sans problème, mais elle est plus rapide et plus simple à coder que l'option de service Web.

Conclusion

Bien que mon exemple de client dans cet article ait été une application Web Forms, les mêmes techniques s'appliquent à une application Windows Forms.

EnterpriseServices est un ensemble de fonctionnalités spécifiques qui peuvent ou non être nécessaires à notre application spécifique. Chacune doit être évaluée individuellement et ne doit pas être utilisée uniquement parce que nous créons une application d'entreprise.

S'il est pertinent d'utiliser certains de ces services dans notre application, nous pouvons facilement sous-classer ServicedComponent, ajouter certains attributs à notre assembly, à notre classe et à nos méthodes, et exploiter les fonctionnalités intégrées d'EnterpriseServices.



Dernière mise à jour le lundi 8 juillet 2002



Pour en savoir plus
Microsoft réalise une enquête en ligne pour recueillir votre opinion sur le site Web de MSDN. Si vous choisissez d’y participer, cette enquête en ligne vous sera présentée lorsque vous quitterez le site Web de MSDN.

Si vous souhaitez y participer,
Afficher:
© 2014 Microsoft