Grundlegendes zu Enterprise Services (COM+) in .NET

 

Shannon Pahl
Microsoft Corporation

April 2002

Zusammenfassung: Enthält technische Details hinter der Integration von Microsoft .NET- und COM+-Diensten und beschreibt die für verwalteten Code verfügbaren Dienste. (26 gedruckte Seiten)

Contents

Einführung
Transaktionen
Bereitstellung
Serviced-Komponenten
Objektlebensdauer
Sicherheit
Remotekomponenten
Zusammenfassung

Einführung

Dieser Artikel erfordert einige Kenntnisse mit den Microsoft®.NET Framework- und COM+-Diensten. Die Vertrautheit mit Enterprise Diensten ist nicht erforderlich, wäre aber hilfreich. Weitere Informationen zu diesen Themen finden Sie unter:

COM bietet eine Möglichkeit zum Schreiben von komponentenbasierten Anwendungen. Es ist bekannt, dass die für das Schreiben von COM-Komponenten erforderlichen Klebarbeiten erheblich und wiederholt sind. COM+ ist nicht so viel über eine neue Version von COM; stattdessen stellt COM+ eine Diensteinfrastruktur für Komponenten bereit. Komponenten werden erstellt und dann in COM+-Anwendungen installiert, um skalierbare Serveranwendungen zu erstellen, die einen hohen Durchsatz mit einfacher Bereitstellung erreichen. (Wenn eine Komponente keine Dienste verwenden muss, sollte sie nicht in einer COM+-Anwendung platziert werden). Skalierbarkeit und Durchsatz werden durch das Entwerfen von Anwendungen von Anfang an erreicht, um Dienste wie Transaktionen, Objektpooling und Aktivitätssemantik zu nutzen.

Die .NET Framework bietet eine weitere Möglichkeit zum Schreiben von komponentenbasierten Anwendungen und verfügt über die Vorteile des COM-Programmiermodells für bessere Toolunterstützung, die allgemeine Sprachlaufzeit (CLR) und eine viel einfachere Codierungssyntax. Auf die COM+-Diensteinfrastruktur kann über verwalteten und nicht verwalteten Code zugegriffen werden. Dienste in nicht verwaltetem Code werden als COM+-Dienste bezeichnet. In .NET werden diese Dienste als Enterprise Services bezeichnet. Die Ableitung einer Klasse von ServicedComponent gibt an, dass Dienste für eine Komponente erforderlich sind. (Wenn eine Komponente keine Dienste verwenden muss, sollte sie nicht von ServicedComponent abgeleitet werden). Die Toolunterstützung hat sich verbessert, um Programmierern das Schreiben von serverbasierten Anwendungen zu ermöglichen, aber die gleichen Probleme der Skalierbarkeit und des Durchsatzes befinden sich weiterhin im Bereich der guten Programmiermethoden. Die Grundidee hinter Den Diensten ist design für Durchsatz und Skalierbarkeit von Anfang an und nutzen Enterprise Services, um diese Entwurfsmuster gegebenenfalls einfach zu implementieren.

Es könnte geltend gemacht werden, dass das Design der Diensteinfrastruktur tatsächlich wenig mit COM oder sogar Komponenten zu tun hat: COM+-Dienste können jetzt auf COM-Komponenten, auf .NET-Komponenten und sogar auf andere Entitäten angewendet werden, die nicht als Komponenten betrachtet werden, z. B. ASP-Seiten oder beliebige Codeblöcke (siehe Die Dienste ohne Komponenten COM+ Feature auf Microsoft Windows ® XP).

Alle com+-Dienste, die heute verfügbar sind, sind für .NET- und COM-Objekte verfügbar. Einige dieser Dienste umfassen, Transaktionen, Objektpooling und Bauzeichenfolgen, JIT, Synchronisierung, rollenbasierte Sicherheit, CRM und BYOT. Eine vollständige Auflistung von Diensten in Microsoft Windows 2000 finden Sie unter "Dienste" von COM+ im Platform SDK. Microsoft Windows XP enthält eine neue Version von COM+, nämlich COM+ 1.5, die zusätzliche Dienste enthält, die auch mit .NET-Komponenten verwendet werden können.

Transaktionen

Um verwaltete Anwendungen zu schreiben, die Dienste verwenden, müssen Klassen, die Dienste benötigen, von ServicedComponent abgeleitet werden und verschiedene benutzerdefinierte Attribute verwenden, um die tatsächlich erforderlichen Dienste anzugeben. In diesem Abschnitt werden diese Konzepte eingeführt und wie sie sich auf das Schreiben von verwaltetem Code auswirken. Eine ausführlichere Erläuterung finden Sie in späteren Abschnitten.

Angenommen, ein Klassenkonto wurde geschrieben (der tatsächliche Code wird später aufgeführt) und befindet sich in der BankComponent-Assembly. Diese Klasse könnte wie folgt verwendet werden:

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

Um den Client zu erstellen, muss der Verweis dem BankComponent-Namespace hinzugefügt werden. Darüber hinaus muss ein Verweis für die System.EnterpriseServices-Assembly hinzugefügt werden– im BankComponentClient-Namespace ruft der Client "Dispose() und den ServicedComponent-Konstruktor auf, die in System.EnterpriseServices definiert sind, nicht die Assembly mit BankComponent. Dies ist eine allgemeine .NET-Anforderung, wenn eine abgeleitete Klasse nicht alle Basisklassenmethoden außer Kraft setzt.

Der BankComponent Server-Code zeigt die Implementierung der Kontoklasse in .NET, die Transaktionen verwendet. Das Klassenkonto abgeleitet von der Klasse System.EnterpriseServices.ServicedComponent. Das Transaktionsattribute markiert die Klasse als erforderlich. Die Synchronisierungs- und JIT-Dienste werden automatisch konfiguriert, da das Transaktionsattribute verwendet wird. Das AutoComplete-Attribut wird verwendet, um anzugeben, dass die Laufzeit die SetAbort-Funktion für die Transaktion automatisch aufrufen muss, wenn während der Ausführung der Methode eine nicht behandelte Ausnahme ausgelöst wird; andernfalls wird eine SetComplete-Funktion aufgerufen. Das ApplicationName-Attribut verknüpft diese Assembly mit einer COM+-Anwendung, die die Dienstkonfigurationsdaten für diese Anwendung speichert. Weitere Änderungen, die für diese Klasse erforderlich sind, werden im Code hervorgehoben.

BankComponent Server

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

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

Der Code im BankComponent Server-Namespace zeigt, wie einfach es ist, COM+-Dienste in .NET zu verwenden. Eine Zusammenfassung des vollständigen Prozesses von der Codierung bis zur Bereitstellung ist unten aufgeführt:

  1. Schreiben Sie die Serverassembly.

  2. Erstellen Sie die Assembly:

    1. Signieren Sie die Assembly. Eine Schlüsseldatei kann einmal für das Projekt generiert werden. Es ist nicht erforderlich, eine für jede Kompilierung zu generieren. Ein Schlüssel kann mithilfe der Eingabeaufforderung von Microsoft .NET und sn.exe erstellt werden:

      sn –k Demos.snk
      
    2. Kompilieren Sie den Code. Ein Verweis muss für System.EnterpriseServices hinzugefügt werden.

  3. Stellen Sie die Anwendung bereit.

    Eine Assembly, die dienstfähige Komponenten verwendet, muss mit dem COM+-Katalog registriert werden. Die ServicedComponent-Klasse und die benutzerdefinierten Attribute sind die beiden wichtigsten Konzepte für den Zugriff auf COM+-Dienste aus verwaltetem Code. Die Konfiguration des Diensts wird im COM+-Katalog gespeichert. Die Objekte befinden sich und führen sie innerhalb des CLR aus. Das verwaltete Objekt und der zugehörige COM+-Kontext werden in Abbildung 1 dargestellt und werden in den nächsten beiden Abschnitten deutlicher.

    Abbildung 1. Dienste, die verwalteten Komponenten zugeordnet sind

    Mit COM+-Komponenten müssen Sie den Katalog manuell konfigurieren, aber mit dienstierten Komponenten kann der Katalog basierend auf den Attributen im Code aktualisiert werden. Eine Assembly kann explizit mit dem Befehlszeilentool regsvcs.exe registriert werden oder Skripts schreiben, die auf eine verwaltete API zugreifen. Weitere Details finden Sie im Abschnitt "Bereitstellungsdetails" unten. Während der Entwicklung wird die XCopy-Bereitstellung als Komfort bereitgestellt, indem Sie einfach die Assembly in das Anwendungsverzeichnis kopieren. Wenn eine Clientanwendung Instanzen von Klassen erstellt, die von ServicedComponent abgeleitet wurden, erkennt die Laufzeit, ob sie die Assembly bereits in einer COM+-Anwendung registriert hat. Wenn sie nicht registriert wurde, wird das lokale Verzeichnis nach der Assembly gesucht und falls gefunden, werden alle dienstierten Komponenten in der Assembly in einer COM+-Anwendung registriert, und die Aktivierung kann dann fortgesetzt werden. Dies ist als lazy Registrierung bekannt, funktioniert jedoch nicht für alle Szenarien. Beispielsweise sind Assemblys, die als COM+-Server-App gekennzeichnet sind, explizite Registrierung (siehe unten) erforderlich, und die lazy-Registrierung funktioniert nicht für nicht verwaltete Clients, die verwaltete Dienstkomponenten aufrufen. Die Lazy-Registrierung ist während der Entwicklungszeit nützlich, andernfalls verwenden Sie Skripts, Code oder RegSvcs, um die Assembly zu registrieren.

  4. Möglicherweise platzieren Sie die Assembly im GAC. Weitere Details finden Sie im Bereitstellungsabschnitt.

  5. Führen Sie den Client aus.

Bereitstellung

Benutzerdefinierte Attribute sind eine der beiden wichtigsten Konzepte für den Zugriff auf COM+-Dienste aus verwaltetem Code. Benutzerdefinierte Attribute werden verwendet, um die erforderlichen Dienste anzugeben, z. B. das benutzerdefinierte Attribut "Transaktion" im vorherigen Codeeintrag. Diese Attribute speichern die Konfigurationsoptionen für einen Dienst in den Assemblymetadaten. Die benutzerdefinierten Attribute werden verwendet, indem sie die Assembly laden und die Spiegelung verwenden, Instanzen der Attribute und Aufrufmethoden für das Attribut erstellen, um die im Attribut gespeicherte Dienstkonfiguration zu extrahieren. Die Informationen können dann im COM+-Katalog geschrieben werden. Der Code, der diese und andere Schritte ausführt, ist in EnterpriseServices.RegistrationHelper enthalten. Um den Prozess der Registrierung zu vereinfachen, verwenden alle Registrierungsformen die Komponente EnterpriseServices.RegistrationHelper. Diese Komponente ist als verwaltete Klasse sowie ein COM-Objekt zugänglich.

Abbildung 2. Registrieren von dienstierten Komponenten

Konzeptual führt RegistrationHelper die folgenden Schritte aus:

  • Verwendet RegistrationServices.RegisterAssembly, um die Assembly in der Registrierung zu registrieren. Daher werden Klassen in der Registrierung als COM-Komponenten angezeigt, die in verwaltetem Code geschrieben wurden und den InprocServer32-Schlüssel auf mscoree.dll zeigen. Wenn eine verwaltete Klasse keine Schnittstellen implementiert, werden die öffentlichen Methoden der Klasse nicht im COM+-Katalog angezeigt, es sei denn, das ClassInterfaceAttribute wird verwendet. Dies bedeutet, dass die Dienstkonfiguration, die der Methodenebene zugeordnet ist, nicht im Katalog gespeichert werden kann. Einige COM+-Dienste können jedoch auf Methodenebene konfiguriert werden und erfordern, dass die Komponente eine Schnittstelle verfügbar macht, die im COM+-Katalog angezeigt wird. Beispielsweise erfordert COM+ rollenbasierte Sicherheit auf Der Methodenebene eine Komponente, um eine Schnittstelle zu implementieren, um den Dienst zu konfigurieren. Weitere Informationen zu diesem Problem werden im Sicherheitsabschnitt behandelt.
  • Generiert eine COM-Typbibliothek aus der Assembly mithilfe von TypeLibConverter. ConvertAssemblyToTypeLib.
  • Registriert die Typbibliothek. Bisher ist dies sehr ähnlich wie RegAsm.exe /tlb.
  • Findet oder erstellt eine COM+-Anwendung. Der Name wird aus dem ApplicationName-Attribut, dem Assemblynamen oder dem angegebenen Anwendungsnamen/GUID extrahiert.
  • Verwendet die Typbibliothek, um die COM+-Anwendung mithilfe der COM+-Administrator-APIs zu konfigurieren.
  • Führt alle benutzerdefinierten Attribute durch und verwendet IConfigurationAttribute, um Konfigurationsdaten für den bestimmten Dienst im COM+-Katalog zu schreiben.

RegistrationHelper versucht, diese Schritte in einer Transaktion mithilfe von RegistrationHelperTx auszuführen, eine Klasse innerhalb einer COM+-Anwendung, die erstellt wird, wenn .NET installiert wird. Wenn die Registrierung fehlschlägt, wird der COM+-Katalog und die Registrierung auf ihren ursprünglichen Zustand wiederhergestellt. Derzeit bleiben die generierten Typbibliotheken jedoch auf dem Datenträger (oder im GAC, wenn sich die Assembly im GAC befindet). Wenn die Assembly auf andere Assemblys verweist, die auch COM+-Dienste verwenden, durchlaufen alle Assemblys im Abhängigkeitsdiagramm die gleichen Schritte wie oben aufgeführt.

Da RegistrationHelper auf den COM+-Katalog zugreift, erfordert er nicht verwaltete Codeberechtigungen und Administratorrechte auf dem Computer. Daher gilt das gleiche für Clients für RegistrierungsHelper: lazy Registrierung, RegSvcs oder Ihre Skripts/Code. Dies bedeutet auch, dass Code, der aus dem Internet heruntergeladen wurde oder auf einer Netzwerkfreigabe gespeichert ist, nicht registriert werden kann.

Es ist möglich, inkompatible Attributkombinationen zu codieren, z. B. die Anforderung einer Transaktions- und Einstellungssynchronisierung, die deaktiviert ist. Diese Kombinationen werden derzeit während der Registrierung beim Schreiben in den COM+-Katalog erkannt und nicht während der Kompilierungszeit. Einige Attribute verfügen über implizite Abhängigkeiten zu anderen Attributen, z. B. bei Verwendung des Transaktionsattributes, dies entspricht der Verwendung der Attribute "Transaction", "JustInTimeActivation" und "Synchronisierung". Wenn eine verwaltete Komponente registriert ist, werden die COM+-Katalogstandardwerte verwendet, es sei denn, Attribute werden verwendet, um die Standardwerte "nicht konfiguriert" zu überschreiben. Wenn beispielsweise eine Komponente registriert ist und kein Transaktionsattribute angegeben ist, wird der nicht konfigurierte Standardwert für die Transaktionseinstellung im Katalog auf "TransactionOption.Disabled" festgelegt. Mit diesem Ansatz kann ein Entwickler ein Attribut aus dem Code entfernen, wenn die Komponente sie nicht mehr benötigt und dann erneut registriert wird, wird der Katalogeintrag für die Transaktion entsprechend zurückgesetzt. Eine detaillierte Liste dieser nicht konfigurierten Standardwerte werden in der Onlinedokumentation angegeben. Standardmäßig konfigurierte Werte sind die Standardwerte in den Parametern eines Attributs, z. B. mit dem Attribut [Transaction] gibt TransactionOption.Erforderlich an.

Da die Konfigurationsdaten für Dienste für verwaltete Klassen im COM+-Katalog gespeichert sind, können bestimmte Katalogeinträge auch nach der Registrierung einer Assembly geändert werden. Einige Dienste sollten auf diese Weise nicht geändert werden. Beispielsweise kann das Deaktivieren des Transaktionsdiensts im Katalog dazu führen, dass der Code falsch ausgeführt wird. Bereitstellungsspezifische Einstellungen wie Objektbauzeichenfolgen und Sicherheitsrollen können nach der Registrierung bearbeitet werden. Die XCopy-Bereitstellung von Assemblys mit Dienstkomponenten ist möglicherweise nicht ausreichend, wenn nach der Registrierung Einstellungen vorgenommen werden. Das COM+-Anwendungsimport- und Exportfeature hilft, den aktuellen Zustand der Anwendung zu verteilen. Weitere Informationen zum Importieren und Exportieren finden Sie im Remoting-Abschnitt.

In einigen Fällen wird der Katalog nicht für Konfigurationsdaten konsultiert, sondern rein aus den Assemblymetadaten extrahiert. Die Fälle sind für AutoComplete, JIT, Object Pooling (obwohl die Poolgröße aus dem Katalog extrahiert wird) und das sichere Methode-Attribut. Weitere Details zu diesem Problem werden in den Abschnitten erläutert, die diese Dienste beschreiben.

Der Prozess der Registrierung einer Assembly generiert automatisch die GUIDs, die COM+ benötigt. Wenn die Assembly nicht signiert ist, werden die GUIDs nur basierend auf Typ- und Namespacenamen generiert. Daher können nicht eindeutige GUIDs generiert werden, wenn die Assembly nicht signiert ist. Ein ähnliches Schicksal wird auf .NET-Assemblys auferlegt, die nicht einmal COM+-Dienste verwenden, sondern eindeutige Typnamen erfordern. Daher müssen assemblies, die COM+-Dienste verwenden, signiert werden. Die Registrierung schlägt fehl, wenn Assemblys nicht signiert sind. Die Registrierung bedeutet auch, dass eine .NET-Klasse, die COM+-Dienste verwendet, einen globalen Konfigurationsdatenspeicher aufweist. Obwohl es möglich ist, private Assemblys in mehrere Anwendungsverzeichnisse zu kopieren, verweisen alle solchen Anwendungen letztendlich auf eine Konfigurationsdaten für eine serviced-Komponente. Daher wirkt sich das Ändern der Konfigurationsdaten im COM+-Katalog auf alle Anwendungen aus, die die Klasse verwenden. Dies ist mit mehreren vroots in einer Microsoft ASP.NET-Konfiguration offensichtlich, die alle eine Kopie derselben Assembly umfassen, die dienstfähige Komponenten verwendet. Eine Möglichkeit, mehrere Konfigurationen für dieselbe COM+-Anwendung zu haben, besteht darin, COM+-Partitionen auf Microsoft Windows .NET zu verwenden. Um den COM+-Partitionsdienst in .NET zu verwenden, verwenden Sie das ApplicationID-Attribut nicht, um die gleiche Komponente in mehreren Partitionen zu installieren, erfordert COM+ eindeutige Anwendungs-IDs.

Im Allgemeinen wird das GAC verwendet, wenn ein Client auf Assemblys zugreifen muss, die sich nicht im gleichen Verzeichnis wie im Clientanwendungsverzeichnis befinden oder wenn die Assembly in einen anderen Prozess geladen wird, der sich nicht im Verzeichnis des Clients befindet. Konzeptionelle, private Assemblys, die dienstfähige Komponenten verwenden, sind tatsächlich freigegebene Assemblys – ihre Verwendung von Konfigurationsdaten, die sie freigegeben haben. Wenn ApplicationActivationOption auf Bibliothek festgelegt ist, ist es möglich, Transaktionen in einer Klasse in einer Assembly zu verwenden und diese Assembly in einem Client zu verwenden, wenn alle Assemblys aus demselben Verzeichnis geladen werden. Wenn eine Assembly, die ApplicationActivationOption verwendet, auf server festgelegt ist, wird die Assembly von dllhost.exe geladen, die sich wahrscheinlich nicht im gleichen Verzeichnis wie der Client befindet. Assemblys, die dienstfähige Komponenten in COM+-Server-Apps verwenden, sollten im GAC platziert werden. Assemblys, die dienstfähige Komponenten in COM+-Bibliotheken verwenden, müssen möglicherweise nicht im GAC platziert werden (es sei denn, sie befinden sich in verschiedenen Verzeichnissen). Die einzige Ausnahme ist das Hosten mit ASP.NET – Assemblys sollten nicht im GAC platziert werden, um die Schattenkopie ordnungsgemäß zu aktivieren.

Um eine .NET-Anwendung zu entfernen, die Dienstkomponenten verwendet, entfernen Sie die Assembly aus dem GAC (wenn sie mit dem GAC registriert wurde), registrieren Sie die Assembly von COM+ mithilfe von regsvcs.exe dann die Assembly und die zugeordneten Typbibliotheken.

Versionsverwaltung

Es ist möglich, GUIDs zu beheben, die COM+ erfordert, indem sie ein GUID-Attribut verwenden. Es wird jedoch empfohlen, die Versionsverwaltung anstelle der expliziten Verwendung von GUIDs zu verwenden. Wenn eine neue Methodensignatur erstellt wird oder wenn Klassen mit unterschiedlichen Dienstattributen versehen werden, sollte die Haupt- oder Nebenversionsnummer der Assembly inkrementiert werden. Die Registrierung sollte einmal für eine bestimmte Version ausgeführt werden. Wenn Sie eine neue Version der Assembly registrieren, werden neue GUIDs für diese Version generiert, und die Komponenten werden in derselben COM+-Anwendung mit demselben Komponentennamen registriert. Die Komponenten werden daher mehrmals in der COM+-Anwendung angezeigt. Jede Komponente verfügt jedoch über eindeutige IDs, die von den GUIDs angegeben werden. Jede Instanz bezieht sich auf eine bestimmte Version der Komponente. Dies wird häufig beim Erstellen von .NET-Anwendungen mit Microsoft Visual Studio ® .NET bemerkt. Die Umgebung fügt dem Projekt das Attribut [assembly: AssemblyVersion("1.0.*") hinzu. Jeder neue Build des Projekts generiert eine neue Buildnummer und daher werden neue GUIDs generiert, wenn die Assembly erneut registriert wird. Daher kann es wünschenswert sein, die Buildnummern bei Bedarf manuell zu erhöhen. Clients binden an eine Assembly mithilfe der CLR-Versionsrichtlinie und daher wird die richtige Version der Klasse in der COM+-Anwendung verwendet. Einige nebenseitige Szenarien beim Schreiben von Assemblys (verwalteten Servern), die dienstfähige Komponenten verwenden, sind: (einige Aspekte der unten verwendeten Aktivierung werden im nächsten Abschnitt beschrieben)

  • Verwalteter Client, verwalteter Server, keine festen GUIDs, die in der Assembly verwendet werden.
  • Der Client lädt die assembly, die von der Versionsrichtlinie angegeben ist.
  • Verwalteter Client, verwalteter Server, feste GUIDs verwendet.
  • Wenn der Client eine Klasse aktiviert und Versionsrichtlinie verwendet, um zu einer alten Version der Assembly zu gelangen, wird die feste GUID im Code während der Aktivierung verwendet, um die Dienstinformationen aus dem Katalog zu extrahieren. Daher werden Informationen aus der letzten registrierten Assembly mit dieser GUID verwendet, um das Objekt zu erstellen, was tatsächlich die neuere Version sein kann und daher eine Typ-Cast-Ausnahme beim Versuch, vom tatsächlich erstellten Objekt (v2) auf den Verweis im Code (v1) zu casten.
  • Verwalteter Client, verwalteter Server, keine feste GUIDs, ändern sie nur die Buildnummer.
  • Obwohl neue GUIDs generiert werden, verfügt die Typbibliothek weiterhin über die gleiche Versionsnummer, da Typbibliotheken nur zwei Zahlen für die Version haben. Dies funktioniert möglicherweise noch, aber wenn Version 2 über Version 1 installiert ist, wird version 1 deinstalliert, die Typbibliothek für Version 2 wird nicht registriert. Lösung 1: Die nächste Version der .NET Framework (V1.1) behebt dieses Problem, indem die Typbibliothek unabhängig von der Assembly versioniert werden kann. Dies bedeutet, dass beim Ändern der Assemblyversionsnummer die Typbibliotheksversion geändert werden sollte. Lösung 2: Verwenden Sie nur Haupt- und Nebenversionsnummern.
  • Nicht verwalteter Client, verwalteter Server, keine behobenen GUIDs verwendet.
    • Der Client verwendet eine GUID zum Erstellen der Komponente. Interop löst die GUID auf einen Namen auf, und die Versionsrichtlinie wird angewendet. Wenn Version 1 und Version 2 einer Assembly auf einem Computer stehen und eine Richtlinie verwendet wird, um auf Version 2 zu gelangen, ruft der nicht verwaltete Client Version 2 ab.
    • Installieren Sie Version 1, installieren Sie Version 2, deinstallieren Sie Version 1. Jetzt kann der Client die Komponente nicht erstellen, es sei denn, es gibt Eine Versionsrichtlinie, die zu Version 2 umgeleitet werden soll. Darüber hinaus müssen Registrierungseinträge für Registrierungsinformationen in Version 1 vorhanden sein. Eine Möglichkeit zum Erstellen von Registrierungsinformationen für eine deinstallierte Version 1 besteht darin, das COM+-Aliasing-Feature auf Windows XP zu verwenden.

Versionierung gilt für alle Komponenten in derselben COM+-Anwendung, d. h. es gibt keine automatische Möglichkeit, die Anwendung selbst zu versionieren. Die Rollen für die Anwendung können beispielsweise nicht mithilfe der Versionsrichtlinie versioniert werden. Verwenden Sie das Anwendungsnamenattribut, um die Anwendungsversionierung zu erledigen.

Serviced-Komponenten

Aktivierung

Die Enterprise Services-Infrastruktur basiert auf dem Konzept eines Kontexts. Ein Kontext ist eine Umgebung für Objekte mit ähnlichen Ausführungsanforderungen. Dienste können während der Aktivierung und/oder während der Methodenaufrufinterception erzwungen werden. Obwohl COM+-Dienste in nicht verwaltetem Code geschrieben werden, ist die Integration von COM+-Diensten mit .NET viel tiefer als die Verwendung der COM-Interoptechnologie in .NET. Ohne ableiten von ServicedComponent würde der Registrierungsprozess nicht den gewünschten Effekt haben.

Serviced-Komponenten können in einer Vielzahl von Kombinationen aktiviert und gehostet werden. Wie in Abbildung 3 dargestellt, bezieht sich diese Diskussion auf drei Fälle, In-Process (dieselbe App-Domäne), app-domänenübergreifende (denselben Prozess) und prozessübergreifende Aktivierungen. Die Bedeutung dieser Fälle ist die Grenzen, die beim Aufrufen von Komponenten überschritten werden. Die In-Process-Aktivierung führt zu einer potenziellen kontextübergreifenden Grenze, der domänenübergreifende Fall verfügt sowohl über Kontext- als auch über App-Domänengrenzen, während der prozessübergreifende Fall mit Computer-/Querprozess- und Kontextgrenzen befasst.

Abbildung 3. Aktivierungshosts für dienstfähige Komponenten

Die Implementierung von Dienstkomponenten basiert auf .NET-Remoting, das einen erweiterbaren Mechanismus zum Anschließen von Diensten bereitstellt, die in nicht verwaltetem oder verwaltetem Code geschrieben wurden. Von ContextBoundObject abgeleitete serviceierte Komponenten und implementieren verschiedene Schnittstellen, z. B. IDisposable. Die Aktivierungskette in der CLR ist einfach mithilfe von ProxyAttribute abgeleiteten benutzerdefinierten Attributen angepasst. Interception kann angepasst werden, indem benutzerdefinierte reale Proxys geschrieben werden. Wenn eine neue abgeleitete Dienstkomponente erforderlich ist, wird die Aktivierungskette angepasst, sodass der Aktivierungsaufruf tatsächlich einen verwalteten C++-Wrapper für CoCreateInstance aufruft. Dadurch kann COM+ die nicht verwalteten Kontexte und Dienste basierend auf den im COM+-Katalog gespeicherten Informationen aus einer zuvor registrierten Assembly einrichten. Dies ist auch die Phase, in der die lazy Registrierung implementiert wird. Während der Registrierung der Assembly verweist der InprocServer32-Schlüssel auf mscoree.dll, wodurch die COM+ CreateInstance letztendlich wieder zur Laufzeit umgeleitet wird, um das echte verwaltete Objekt zu erstellen. Daher wird während der Aktivierung ein benutzerdefiniertes reales Proxyobjekt erstellt. Die In-Process-Version dieses Proxys wird als Dienstkomponentenproxy oder SCP bezeichnet. Dies wird in Abbildung 4 dargestellt.

Abbildung 4. Aktivierungspfad

Der Rückgabepfad vom Aktivierungsaufruf erzwungen die verwalteten Verweise aus verwaltetem Code, über nicht verwaltetes COM+ und zurück in verwalteten Code (der umgekehrte Pfad der Zeile 1 in Abbildung 4). Je nachdem, wo das reale Objekt erstellt wurde, hebt der Client den Verweis in das relevante Formular auf. Bei der In-Process-Aktivierung gibt Abbildung 5 an, dass der Verweis als direkter Verweis auf den transparenten Proxy (TP) aufgehoben wird. Domänenübergreifende Verweise werden als .NET-Remotingproxy aufgehoben. Cross process or cross machine references (Abbildung 6) erfordern mehr, dass sie nichtmarshaliert werden müssen: COM-Interop führt Aufrufe von IManagedObject aus, die während der Aktivierung und Entmarung von ServicedComponent implementiert werden. Der Remote serviced component proxy (RSCP) führt während der Aktivierung Aufrufe auf IServicedComponentInfo aus, um den URI des Serverobjekts abzurufen, was bedeutet, dass zwei Remoteaufrufe während der Aktivierung ausgeführt werden. Wenn COM+-rollenbasierte Sicherheit auf Methodenebene erforderlich ist, müssen diese Schnittstellen einer Rolle zugeordnet werden, damit die Unmarshaling erfolgreich verläuft, wenn die Infrastruktur Aufrufe dieser Schnittstellen vornimmt. Im Sicherheitsabschnitt werden die Auswirkungen erläutert, die die Prozessaktivierung und das Marshalling zum Konfigurieren der rollenbasierten Sicherheit hat.

Abbildung 5. Infrastruktur für Prozessaufrufe

Abbildung 6. Infrastruktur für Abwesenheitsaufrufe

Die Aktivierungskette wurde daher angepasst, um einen benutzerdefinierten realen Proxy zu erstellen (verwendet für die Abfangen) und um die nicht verwalteten Kontexte zu erstellen und COM+ nur mit der Kontextinfrastruktur zu verlassen, die zum Ausführen der Semantik der Abfangendienste erforderlich ist. Der COM+-Kontext ist jetzt einem verwalteten Objekt zugeordnet, nicht einem COM-Objekt.

Interception

Abbildung 7 stellt die In-Process-Methodenaufrufinfrastruktur dar. Mit dem benutzerdefinierten Proxy (SCP) können verwaltete Aufrufe abgefangen werden. Während der Aktivierung wird die COM+-Kontext-ID im SCP gespeichert. Wenn ein verwaltetes Objekt eine Dienstkomponente aufruft, wird die kontext-ID, die im Ziel-SCP gespeichert ist, mit der Kontext-ID des aktuellen Kontexts verglichen. Wenn die Kontext-IDs identisch sind, wird der Aufruf direkt auf dem realen Objekt ausgeführt. Wenn die Kontext-IDs unterschiedlich sind, ruft der SCP COM+ auf, um die Kontexte zu wechseln und den Dienst zum Eingeben des Methodenaufrufs zu rendern. Bei prozessinternen Aufrufen ähnelt dies AppDomain.DoCallBack, aber bei AppDomain ist COM+. Die Funktion "DoCallBack" gibt zuerst COM+ (Schritt 2 in Abbildung 7) ein, wodurch der Kontext wechselt und der Dienst gerendert wird, und die Rückruffunktion ruft dann das SCP auf. Der SCP führt das Daten marshalling aus und ruft die Methode für das reale Objekt auf. Wenn die Methode beendet wird, kann der Rückgabepfad COM+ die Semantik rendern, um einen Methodenaufruf zu verlassen (Schritt 5 in Abbildung 7). COM+ wird nur zum Rendern des Diensts verwendet. Daten marshalling und der Methodenaufruf wird innerhalb der .NET-Laufzeit*,* ausgeführt, sodass beim Aufrufen von Methoden keine Konvertierungstypen wie String in BSTR erforderlich sind. Daten marshalling wäre erforderlich, wenn COM-Interop für Prozessaufrufe verwendet wurde*.* Der Aufruf zum Rendern des Diensts in nicht verwaltetem Code ist daher kein COM-Interopaufruf für In-Process-Aufrufe.

Abbildung 7. Infrastruktur für Prozessaufrufe

Aufrufe statischer Methoden werden nicht an die transparenten und realen Proxys weitergeleitet. Daher können statische Methoden keine Interception-Dienste verwenden; Stattdessen werden sie im Kontext des Clients aufgerufen. Interne Methoden werden innerhalb des richtigen Kontexts aufgerufen, d. h. ein Client, der eine interne Methode für ein objekt aufruft, das für eine neue Transaktion konfiguriert ist, nimmt an einer neuen Transaktion teil. Da Methodenebenendienste jedoch eine Schnittstelle im COM+-Katalog erfordern (mehr zu diesem Thema weiter und in der Sicherheit), können interne Methoden für Methodenebenendienste nicht konfiguriert werden. Dienste können auf Eigenschaften angewendet werden, aber die Attribute der Methodenebene (z. B. AutoVervollständigen) müssen einzeln auf den Getter- und Settermethoden platziert werden.

Das AutoVervollständigen-Attribut ist eine bequeme Möglichkeit, Transaktionen zu verwenden, ohne Code zu schreiben, um auf den Dienst zuzugreifen. Alternativ kann ContextUtil.SetAbort oder ContextUtil.SetComplete verwendet werden. Dieser Dienst kann im COM+-Explorer konfiguriert werden, indem ein Kontrollkästchen für die Eigenschaften der Methode festgelegt wird. Verwaltete Objekte müssen jedoch keine Schnittstellen implementieren. Dies gilt auch für dienstfähige Komponenten. Wenn die Methode nicht auf einer Schnittstelle deklariert wird, kann die Konfiguration für Methodenebenendienste nicht in den Katalog für die Registrierung geschrieben werden; die Konfiguration kann nur in Metadaten gespeichert werden. Wenn keine Schnittstelle für die Methode vorhanden ist, erfolgt der Kontextwechsel vom SCP mithilfe der konfigurationsinformationen, die auf IRemoteDispatch.RemoteDispatchAutoDone gespeichert sind, wenn das AutoComplete-Attribut vorhanden ist. Wenn AutoComplete nicht vorhanden ist, wird IRemoteDispatch.RemoteDispatchNotAutoDone verwendet. IRemoteDispatch ist eine Schnittstelle, die von ServicedComponent implementiert wird. Nicht verwaltete Clients können nur dienstfähige Komponenten aufrufen, die keine Schnittstellen mit IDispatch (spät gebunden) verwenden und daher die AutoVervollständigen-Semantik aufgrund des Fehlens eines echten Proxys in diesem Fall nicht erzwungen werden kann. Auch wenn Schnittstellen verwendet werden, wird die Konfiguration von AutoVervollständigen weiterhin von den Metadaten für verwaltete Clients gesteuert. Der DCOM-Methodenaufruf wird nur im Prozessfall auf RemoteDispatchAutoDone ausgeführt. Out-of-Process-Komponenten verwenden nicht den DoCallBack-Mechanismus, sondern DCOM kann verwendet werden, um den Aufruf bereitzustellen und den Dienst zu rendern. Wenn sich die Methode auf einer Schnittstelle befindet, wird die Schnittstellenmethode für die Remotedienstkomponente mithilfe von DCOM aufgerufen, andernfalls wird der Aufruf an die IRemoteDispatch-Schnittstelle auf ServicedComponent verteilt. Dies bedeutet, dass auch Aufrufe wie Dispose() über DCOM aufgerufen werden, die Auswirkungen, die später erläutert werden.

Kontexte

Die Klasse ContextUtil wird verwendet, um auf den zugeordneten COM+-Objektkontext und dessen Eigenschaften zuzugreifen. Dies bietet ähnliche Funktionen wie das von CoGetObjectContext zurückgegebene Objekt im nicht verwalteten Code. Der verwaltete Objektkontext, der einer Dienstkomponente zugeordnet ist, dient einem anderen Zweck als der zugeordnete nicht verwaltete Objektkontext. Dies wird manifestiert, indem drei verwaltete Objekte geschrieben werden, eine mit Transaktionen erforderlich (die als Stamm fungieren), die anderen beiden nicht von der Dienstkomponente abgeleitet werden (als untergeordnete Objekte, die kontext-agile verwaltete Objekte veranschaulichen). Die Nicht-serviced-Komponenten verhalten sich so, als ob sie Dienstkomponenten mit unterstützten Transaktionen waren, d. h. sie können Aufrufe an Ressourcenmanager tätigen und bei Bedarf ContextUtil.SetAbort verwenden. Wenn das Stammobjekt erstellt wird, wird der zugeordnete nicht verwaltete Kontext erstellt und dem aktuellen Thread zugeordnet. Wenn ein Aufruf der untergeordneten Objekte vorgenommen wird, da sie keinem nicht verwalteten Kontext zugeordnet sind, ist keine COM+-Kontextänderung erforderlich, sodass der Thread weiterhin die nicht verwaltete Kontext-ID des Stamms verwaltet. Wenn ein untergeordnetes Objekt ressourcenmanager aufruft, extrahieren die Ressourcenmanager wiederum den nicht verwalteten Kontext aus dem Thread, der das untergeordnete Objekt ausführt, das den nicht verwalteten Kontext des Stammobjekts darstellt. Das Vertrauen auf dies ist gefährlich, und in zukünftigen Versionen kann der nicht verwaltete Kontext mit dem verwalteten Kontext zusammengeführt werden, und daher werden die untergeordneten Objekte einem potenziell anderen verwalteten Kontext zugeordnet; die Ressourcenmanager nehmen den Kontext des Stammobjekts nicht auf. Daher könnte ein Upgrade auf eine neue Version von .NET Code unterbrechen, der von diesem Verhaltenstyp abhängt.

Leistungsergebnisse

In diesem Abschnitt wird die Leistung des verwalteten Clients, verwaltete Server-Komponentenlösung, mit der nicht verwalteten Client-/Serverlösung verglichen. Der In-Process-Fall wird in der folgenden Tabelle beschrieben. Ein für Transaktionen konfigurierter ServicedComponent wurde in C# mit einer einzigen Methode geschrieben, die einfach Zahlen hinzufügt. Eine entsprechende C++-Implementierung wurde zum Vergleich verwendet. Dieser Vergleich zeigt den Unterschied zwischen verwalteten und nicht verwalteten Lösungen ohne echte Arbeit. In-Process-Aktivierungen sind etwa 3,5 Mal langsamer in der verwalteten Lösung und Methodenaufrufe sind etwa 2 mal teurer, wenn ein Kontextwechsel vorhanden ist. Beim Vergleichen von Serviced-Komponentenmethodenaufrufen, die einen Kontextwechsel erfordern, gibt es jedoch ca. 3 Größenordnungsunterschiede, die den Erfolg der In-Process-Komponenten-Interception-Infrastruktur angeben. Aus Prozesslösungen werden Aktivierungen etwa 2 mal teurer und kontextübergreifende Aufrufe werden etwa 3 mal teurer.

Tabelle 1 zeigt skalierte Zeiten für Prozessaktivierungs- und Methodenaufrufe mit verwalteten und nicht verwalteten Lösungen an.

Tabelle 1. In-Process-Aktivierungs- und Methodenaufrufe

  Verwaltete Lösung Nicht verwaltete Lösung
Aktivierung 35 10
Aufruf der über kontextübergreifenden Do-Nothing-Methode 2 1
Aufruf der kontextübergreifenden Do-Work-Methode 200 100

Aktivierungen sind etwa eine Reihenfolge höher als Methodenaufrufe für die Methode "Do-Nothing". Wenn Sie einfach eine DTC-Transaktion abrufen (aber nichts damit tun), wird die Aktivierungs- und Methodenaufrufzeiten auf dieselbe Reihenfolge der Größe angewendet. Wenn der Methodenaufruf einfach eine datenbankpoolierte Datenbankverbindung öffnet, ist die Methodenaufrufarbeit dann eine Größenordnung größer als der Aktivierungs- und "Do-Nothing"-Methodenaufruf kombiniert, um zu beweisen, dass der Aufwand der Serviced-Komponenteninfrastruktur theoretisch ist, wenn dem Experiment echte Arbeit hinzugefügt wird.

Objektlebensdauer

Just-In-Time-Aktivierung

Der Just-in-Time-Dienst (JIT) wird in der Regel nicht isoliert verwendet. Sie wird implizit mit dem Transaktionsdienst und am häufigsten mit Objektpooling verwendet. In diesem Beispiel werden jedoch einige interessante Themen hervorgehoben. Im folgenden Code wird eine .NET-Klasse geschrieben, die nur den JIT-Dienst verwendet.

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

Die Klasse leitet sich von ServicedComponent ab und verwendet das JIT-Attribut, um den erforderlichen Dienst anzugeben. Um die Methoden "Activate" und "Deactivate" im nicht verwalteten Code außer Kraft zu setzen, ist die Klasse erforderlich, um die IObjectControl-Schnittstelle zu implementieren. Die Klasse ServicedComponent verfügt stattdessen über virtuelle Methoden, die zum Behandeln der Aktivierungs- und Deaktivierungsereignisse überschrieben werden können. Allerdings implementieren weder ServicedComponent noch sein realer Proxy SCP IObjectControl. Stattdessen erstellt der SCP einen Proxy-Tearoff, wenn die IObjectControl-Schnittstelle von COM+ angefordert wird. Die Aufrufe von COM+ auf dem Tearoff werden dann an die virtuellen Methoden des ServicedComponent weitergeleitet. The DeactivateOnReturn bit is set using either the AutoComplete attribute on the method, calling ContextUtil.SetComplete(), ContextUtil.SetAbort() or setting ContextUtil.DeactivateOnReturn. Vorausgesetzt, das DeactivateOnReturn-Bit wird während jedes Methodenaufrufs festgelegt, lautet die Abfolge von Methodenaufrufen: der Konstruktor der Klasse, Activate, der tatsächliche Methodenaufruf, Deactivate, Dispose(true) und schließlich der Finalizer der Klasse, falls vorhanden. Die gleiche Sequenz wird wiederholt, wenn ein anderer Methodenaufruf ausgeführt wird. Eine gute Entwurfspraxis besteht darin, die Methoden "Aktivieren" und "Deaktivieren" nur zu überschreiben, um zu wissen, wann das Objekt herausgenommen und wieder in den Objektpool versetzt wird. Die verbleibende Logik von Activate and Deactivate sollte in den Methoden des Konstruktors und Dispose(bool) der Klasse platziert werden. Das DeactivateOnReturn-Bit kann mithilfe einer der folgenden Ansätze festgelegt werden:

  1. Der Client verwendet den Status des Objekts nur für einen einzelnen Methodenaufruf. Beim Eintrag zur Methode wird ein neues reales Objekt erstellt und an den SCP angefügt. Beim Beenden der Methode wird das reale Objekt deaktiviert, indem zuerst Aufrufe an Dispose(true) gefolgt von dem tatsächlichen Objektdefinator ausgeführt werden, sofern vorhanden. Der zugeordnete COM+-Kontext, SCP und TP bleiben jedoch lebendig. Der Clientcode behält weiterhin seinen Verweis auf das, was er glaubt, ein tatsächliches Objekt (der transparente Proxy). Der nächste Methodenaufruf des Clients auf demselben Verweis führt dazu, dass ein neues reales Objekt erstellt und an den SCP angefügt wird, um den Methodenaufruf zu serviceieren (siehe Abschnitt zum Objektpooling, um die Anforderung zum Erstellen eines neuen Objekts zu entfernen). Um das reale Objekt zu deaktivieren, muss das reale Objekt die Fertigkeit angeben, wenn ein Methodenaufruf beendet wird. Dies kann mithilfe von:
    1. das AutoVervollständigen-Attribut für eine Methode der Klasse
    2. eine von zwei Methodenaufrufen der ContextUtil-Klasse, DeactivateOnReturn oder SetComplete
  2. Der Client führt mehrere Methodenaufrufe für dasselbe Objekt aus, ohne das Objekt nach jedem Methodenaufruf zu deaktivieren, indem der Vorgang bit auf "false" festgelegt wird, bevor die Methode beendet wird. Wenn Sie z. B. eine Dienstkomponente, die JIT auf Formularebene verwendet, und zwei Aufrufmethoden für Formularschaltflächen auf derselben Objektinstanz verwenden, indem sie die Methoden explizit auf "false" festlegen. An einem bestimmten Punkt sollte die Fertigkeit auf "true" festgelegt werden. Dieser Ansatz impliziert, dass zwischen dem Client und dem Objekt ein Vertrag besteht. Dies kann implizit oder explizit vom Client erfolgen:
    1. Der Client weiß, dass eine bestimmte Methode für das Objekt aufgerufen wird, wenn es ausgeführt wird, um das Objekt zu deaktivieren. Die Methodenimplementierung verwendet die Ideen in Option 1. Der Objektverweis kann erneut mit derselben Aufrufsequenz aufgerufen werden, was bedeutet, dass ein neues reales Objekt erstellt wird.
    2. Das Objekt wird vom Client explizit zerstört, wenn es die Dispose()-Methode für das Objekt aufruft. Dispose() ist eine Methode, die für ServicedComponent und umgekehrte Aufrufe Dispose(true) definiert ist, der Finalizer der Klasse (sofern vorhanden) und dann den zugehörigen COM+-Kontext herunterreißt. In diesem Fall können keine weiteren Methodenaufrufe für den Objektverweis vorgenommen werden. Eine Ausnahme wird ausgelöst, wenn dies versucht wird. Wenn viele Clients dasselbe Objekt verwenden, sollte das Aufrufen von Dispose() nur ausgeführt werden, wenn der letzte Client mit dem Objekt abgeschlossen ist. Die zustandslose Natur von JIT-Objekten führt jedoch zu Entwurfspraktiken zu einer einzelnen Instanz pro Clientmodell.
    3. Das Objekt legt seine Fertigkeit niemals auf "true" fest, und der Client ruft niemals Dispose() auf. Das reale Objekt, Proxys und Kontext werden zerstört, wenn die Garbage Collection stattfindet. Die vom GC initiierte Methodenaufrufreihenfolge wäre "Deactivate", "Dispose(false") und dann der Kursendizer (sofern vorhanden).

Alle dienstierten Komponenten verfügen über einen zugehörigen COM+-Kontext, der als Verweis im SCP (oder RSCP im Remotefall) gespeichert wird. Der Verweis wird nur veröffentlicht, wenn der GC stattfindet oder wenn der Client Dispose() aufruft. Es ist besser, sich nicht auf den GC zu verlassen, um den Kontext zu bereinigen: Der COM+-Kontext enthält einen Betriebssystemhandpunkt und einige Arbeitsspeicher, der die Veröffentlichung dieser Handle möglicherweise verzögert, bis ein GC auftritt. Auch wenn ServicedComponent keinen Finalizer besitzt, implementiert der SCP einen Finalizer, was bedeutet, dass der COM+-Kontextverweis niemals garbage collection sammelt. Tatsächlich wird der Kontext, wenn der Finalizer auf dem SCP schließlich aufgerufen wird, immer noch nicht durch den Finalizerthread zerstört, sondern die Arbeit der Zerstörung von Kontexten aus dem Finalizerthread entfernt und in einer internen Warteschlange platziert. Dies wurde getan, da es festgestellt wurde, dass der Finalizerthread in bestimmten Stressumgebungen genutzt werden kann, in denen Dienstkomponenten schnell erstellt, verwendet und außerhalb des Bereichs gehen. Stattdessen löscht ein interner Thread die Warteschlange und zerstört alte Kontexte. Darüber hinaus versucht jeder Anwendungsthread, der einen neuen ServicedComponent erstellt, zuerst versucht, ein Element aus der Warteschlange zu entfernen und einen alten Kontext zu zerstören. Daher wird der Aufruf von Dispose() vom Client den COM+-Kontext schneller mithilfe des Clientthreads herunterreißen und die Handle- und Speicherressourcen freigeben, die der Kontext verwendet. Manchmal kann Dispose() Ausnahmen auslösen. Ein Fall ist, wenn das Objekt in einem Nicht-Stammtransaktionskontext lebt, der abgebrochen wurde– der Dispose()-Aufruf kann eine CONTEXT_E_ABORTED Ausnahme beobachten. Ein weiterer Fall wird in objektpooling erläutert.

Aus Leistungsgründen ist es besser, einen Finalizer in einer abgeleiteten ServicedComponent-Klasse zu implementieren und stattdessen diese Logik in der Dispose(bool)-Methode zu platzieren. Obwohl der SCP einen Finalizer implementiert, wird der Finalizer des realen Objekts mithilfe von Spiegelung aufgerufen.

Eine gute Entwurfspraxis für die Verwendung von JIT besteht darin, Folgendes zu verwenden:

  • Platzieren Sie benutzerdefinierten Aktivierungs- und Endisierungscode in den Methoden Konstruktor und Dispose(bool), nicht um einen Finalizer zu implementieren und ein einzelnes Aufrufmuster zu verwenden, indem die Fertigkeit mithilfe des AutoComplete-Attributs für die Methode angegeben wird.
  • Rufen Sie "Dispose() vom Client auf, wenn der Client mit dem Objekt fertig ist.

Die Diskussion hat angenommen, dass der Client verwaltet wird und die Komponente in Bearbeitung ist. Wenn die Komponente nicht verarbeitet wird: (Weitere Details werden im Abschnitt "Remoting" beschrieben)

  • Die GC bereinigt nur die Objekte, wenn ihre .NET-Remoting-Leasezeit für clientaktive Objekte abgelaufen ist.
  • Wie bereits erwähnt, wird DCOM beim Aufrufen von Methoden für Out-of-Process-Komponenten verwendet, um den Kontext zu wechseln und den Methodenaufruf bereitzustellen. Wenn die Komponente zuvor von JIT deaktiviert wurde und dann ein Aufruf von Dispose() erfolgt, wird der Serverkontext eingegeben, und das eigentliche Objekt wird erneut erstellt, um den DCOM-Aufruf zu bedienen und schließlich wieder deaktiviert. Wenn das reale Objekt deaktiviert wurde, wird bei prozessinternen Komponenten kein Versuch unternommen, zum richtigen Kontext zu wechseln, bevor der Dispose()-Aufruf gewartet wird (was die Komponente erneut aktivieren würde), stattdessen wird nur der Kontext zerstört.

Objektpooling

Die grundlegende Lokale objektpooling ist die Wiederverwendung von Objekten. Objektpooling wird am häufigsten mit JIT verwendet. Dies gilt sowohl für poolierte COM-Komponenten als auch für poolierte .NET-Komponenten.

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

Wie bei der Verwendung von JIT kann die Objektpooling auf eine von zwei Arten verwendet werden:

  1. Einzelnes Anrufmuster. Im Code wird das Objekt aus dem Pool abgerufen, wenn der Client versucht, einen Methodenaufruf zu tätigen und an den Pool zurückzugeben, der beim Verlassen des einzelnen Methodenaufrufs zurückgegeben wird, vorausgesetzt, JIT wird mit Objektpooling verwendet, und der Erledigtheitsbit wird während des Methodenaufrufs auf "true" festgelegt. Auch hier gelten die gleichen einzelnen Anrufansätze für die Verwendung von JIT. Der Konstruktor wird nur einmal aufgerufen, wenn das Objekt erstellt und im Pool platziert wird. Die Methodenaufrufreihenfolge bei der Verwendung von JIT- und Poolobjekten lautet: Activate, der Methodenaufruf, "Deactivate" und "CanBePooled". Wenn CanBePooled "true" zurückgibt, wird das Objekt wieder in den Pool versetzt (obwohl der Kontext wie zuvor erläutert weiterhin lebendig bleibt). Die gleiche Methodenaufrufreihenfolge wird für nachfolgende Methodenaufrufe wiederholt (ohne den Konstruktor erneut aufrufen), nachdem ein beliebiges Objekt aus dem Pool extrahiert wurde (serviced components kann keine parametrisierten Konstruktoren verwenden). Wenn der Client "Dispose()" für das poolierte Objekt aufruft, wird nur der Kontext im Prozessfall zerstört. Wie bereits erwähnt, kann der Aufruf von Dispose() das Objekt erneut aktivieren. Wenn das Objekt pooled ist, muss ein Objekt aus dem Pool abgerufen werden, d. h., dass Dispose() eine Ausnahme mit CO_E_ACTIVATION_TIMEOUT auslösen kann.
  2. Multi-Call-Muster. Mit ähnlichen Methodenaufrufansätzen, die im JIT-Dienst hervorgehoben sind, kann das Objekt erst nach einer Reihe von Methodenaufrufen für das Objekt in den Pool zurückgesetzt werden. Wenn der Client jedoch "Dispose" nicht aufruft und JIT nicht verwendet wird, gibt es keine Möglichkeit, sicherzustellen, dass die untergeordneten Objekte des pooled-Objekts, die die Fertigstellung erfordern, wieder aufgenommen werden können, wenn das Objekt vom GC in den Pool zurückgesetzt wird. Wenn das pooled-Objekt garbage collection ist, würde es auch keine Garantie innerhalb von Deactivate geben, welche Member weiterhin gültig sind. In der nächsten Version der .NET Framework (V1.1) werden canBePooled und Deactivate nicht aufgerufen, und das Objekt wird nicht in den Pool eingefügt. Bei diesem Ansatz gibt es ein konsistenteres Modell – in "Deaktivieren untergeordneter Objekte" wird in Dispose() nicht garantiert, dass untergeordnete Objekte lebendig werden. Daher ist es wichtig, dass Dispose() für poolierte Objekte aufgerufen wird, die nicht JIT verwenden, andernfalls wird das Objekt nicht an den Pool zurückgegeben.

Es ist akzeptabel, dass ein Administrator die Poolgröße und Timeouts ändern kann, nachdem die Assembly bereitgestellt und registriert wurde. Die Änderungen an der Poolgröße werden wirksam, wenn der Prozess neu gestartet wird. Auf Windows XP oder besser gilt die Poolgröße für jede Anwendungsdomäne innerhalb des Prozesses. Bei Windows 2000 wird die Poolgröße mit poolierten Objekten in der Standardanwendungsdomäne verarbeitet, was bedeutet, dass der Client effektiv über die App-Domäne hinweg an das poolierte Objekt kommuniziert, wenn ein pooliertes Objekt innerhalb desselben Prozesses benötigt wird. Eine Realisierung ders ist die Verwendung von poolierten .NET-Objekten, die in einer COM+-Bibliotheksanwendung aus einer ASP.NET Anwendung definiert sind, in der jede IIS-vroot in separaten Anwendungsdomänen untergebracht ist.

Serviced-Komponenten können keine parametrisierten Konstruktoren verwenden.

Sicherheit

Codezugriffssicherheit (Code Access Security, CAS)

Die .NET Framework Sicherheit ermöglicht den Zugriff auf Code nur dann, wenn er über die Berechtigung verfügt, auf Ressourcen zuzugreifen. Um dies auszudrücken, verwendet die .NET Framework das Konzept der Berechtigungen, die das Recht auf Code für den Zugriff auf geschützte Ressourcen darstellen. Code fordert die erforderlichen Berechtigungen an. Die .NET Framework bietet Codezugriffsberechtigungsklassen. Alternativ können benutzerdefinierte Berechtigungsklassen geschrieben werden. Diese Berechtigungen können verwendet werden, um den .NET Framework anzugeben, was der Code tun muss, und um anzugeben, was die Aufrufer des Codes tun müssen. Jeder Codepfad über System.EnterpriseServices fordert nicht verwaltete Codeberechtigungen an.

Die Codezugriffssicherheit in .NET ist in Anwendungen am nützlichsten, in denen Code aus dem Web heruntergeladen wird und der Autor möglicherweise nicht vollständig vertrauenswürdig ist. In der Regel sind Anwendungen, die Dienstkomponenten verwenden, vollständig vertrauenswürdig, erfordern die Sicherheit, zwischen mehreren Prozessen zu fließen und die Konfiguration von Rollen zur Bereitstellungszeit zu aktivieren. Diese Features werden von COM+ rollenbasierter Sicherheit verfügbar gemacht.

Jeder Codepfad über System.EnterpriseServices erfordert nicht verwaltete Codeberechtigungen. Dies impliziert Folgendes:

  • Die Berechtigung "Nicht verwalteter Code" ist erforderlich, um Kontextübergreifende Aufrufe von Dienstkomponenten zu aktivieren und auszuführen.
  • Wenn ein Verweis auf eine dienstfähige Komponente an nicht vertrauenswürdigen Code übergeben wird, können methoden, die für ServicedComponent definiert sind, nicht aus dem nicht vertrauenswürdigen Code aufgerufen werden. Benutzerdefinierte Methoden, die für eine von ServicedComponent abgeleitete Klasse definiert sind, können jedoch unter bestimmten Umständen von nicht vertrauenswürdigem Code aufgerufen werden: Aufrufe von nicht vertrauenswürdigem Code können für diese benutzerdefinierten Methoden ausgeführt werden, die keine Kontextwechsel erfordern, Abfangendienste und wenn die Implementierung der Methode keine Aufrufe an Mitglieder von System.EnterpriseServices ausführt.

Darüber hinaus wird der Sicherheitsstapel in .NET Version 1 nicht kopiert, wenn ein Threadwechsel ausgeführt wird, sodass benutzerdefinierte Sicherheitsberechtigungen nicht in dienstierten Komponenten verwendet werden sollten.

Role-Based Sicherheit (RBS)

System.EnterpriseServices stellt Sicherheitsdienste für .NET-Objekte bereit, die die Funktionalität der COM+-Sicherheitsmechanismen spiegeln. Wenn eine COM+-Serveranwendung zum Hosten der Komponenten verwendet wird, erfordert die RBS-Features, dass das DCOM-Transportprotokoll verwendet wird, um die Komponenten von einem Remoteclient zu aktivieren. Weitere Details zum Remoting werden im nächsten Abschnitt bereitgestellt. Der Sicherheitsaufrufkontext und die Identität in COM+ ist daher für verwalteten Code verfügbar. Darüber hinaus sind CoImpersonateClient, CoInitializeSecurity und CoRevertClient vertraute Aufrufe, die in der Regel auf der Serverseite verwendet werden, während CoSetProxyBlanket in der Regel auf der Clientseite verwendet wird.

Bestimmte Sicherheitseinstellungen werden nicht in Metadaten mithilfe von Attributen gespeichert, z. B. durch Hinzufügen von Benutzern zu Rollen und Festlegen der Prozesssicherheitsidentität. Die Attribute der Assemblyebene können jedoch verwendet werden, um das, was auf der Sicherheitsregisterkarte des COM+-Explorers für eine COM+-Serveranwendung angezeigt wird, zu konfigurieren:

  • Aktivieren der Autorisierung für die Anwendung (ApplicationAccessControlAttribute(bool)). Dies ist erforderlich, um RBS zu unterstützen.

  • Die Sicherheitsstufe (ApplicationAccessControlAttribute(AccessChecksLevelOption)). Wenn sie auf AccessChecksLevelOption.Application festgelegt ist, werden Benutzer, die Rollen in der Anwendung zugewiesen sind, dem Prozesssicherheitsdeskriptor und der feinkörnigen Rollenüberprüfung auf komponenten-, Methoden- und Schnittstellenebenen hinzugefügt. Sicherheitsüberprüfungen werden daher nur auf Anwendungsebene ausgeführt, und Bibliotheksanwendungen basieren auf dem Hostprozess für die Sicherheit auf Prozessebene. Wenn das Attribut auf AccessChecksLevelOption.ApplicationComponent festgelegt ist, werden Benutzer, die Rollen in der Anwendung zugewiesen sind, dem Prozesssicherheitsdeskriptor hinzugefügt, und rollenbasierte Sicherheitsüberprüfungen werden auf der Anwendung ausgeführt. Außerdem müssen Zugriffsüberprüfungen für jede Komponente aktiviert werden, die RBS erfordert, indem das ComponentAccessControl-Attribut auf die Klasse angewendet wird. In einer Bibliotheksanwendung werden rollenbasierte Sicherheitsüberprüfungen ausgeführt, als ob es sich um eine Serveranwendung handelte. Die Sicherheitseigenschaft ist im Kontext für alle Objekte in der Anwendung enthalten, und der Sicherheitsaufrufkontext ist verfügbar. Wenn ein Objekt eine Konfiguration hat, die mit dem Kontext seines Erstellers nicht kompatibel ist, wird es im eigenen Kontext aktiviert. Die programmgesteuerte rollenbasierte Sicherheit basiert auf der Verfügbarkeit des Sicherheitsaufrufkontexts.

    Wenn eine sinnvolle Zugriffsüberprüfung für COM+-Bibliotheksanwendungen funktioniert, wählen Sie die Zugriffsüberprüfungen auf Prozess- und Komponentenebene aus.

  • Die Identitätswechsel - und Authentifizierungsauswahl entsprechen den Eigenschaften ImpersonationLevel und Authentication des ApplicationAccessControl-Attributs.

    Das SecurityRole-Attribut kann auf die Assembly-, Klassen- oder Methodenebene angewendet werden. Wenn sie auf Assemblyebene angewendet werden, können Benutzer in dieser Rolle eine beliebige Komponente in der Anwendung aktivieren. Wenn sie auf die Klassenebene angewendet werden, können Benutzer in dieser Rolle darüber hinaus eine beliebige Methode für die Komponente aufrufen. Anwendungs- und Klassenebenenrollen können in Metadaten konfiguriert werden oder administrativer Weise durch Zugriff auf den COM+-Katalog.

    Konfigurieren von RBS auf Assemblyebene mithilfe von Metadaten:

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

    Konfigurieren von RBS auf Klassenebene in Metadaten:

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

    RBS auf Assembly- oder Klassenebene kann administratorisch konfiguriert werden, da diese Entitäten im COM+-Katalog vorhanden sind, nachdem die Assembly registriert wurde. Wie bereits erwähnt, werden Klassenmethoden jedoch nicht im COM+-Katalog angezeigt. Um RBS für Methoden zu konfigurieren, muss die Klasse Methoden einer Schnittstelle implementieren und das SecureMethod-Attribut auf Klassenebene oder SecureMethod oder SecurityRole auf Der Methodenebene verwenden. Darüber hinaus müssen die Attribute in der Klassenmethodenimplementierung angezeigt werden, nicht die Schnittstellenmethode in der Definition der Schnittstelle.

  • Die einfachste Möglichkeit, RBS für Methoden zu verwenden, besteht darin, das SecureMethod-Attribut auf Klassenebene anzuwenden und dann Rollen zu konfigurieren (entweder administratorisch oder durch Platzieren des SecurityRole-Attributs auf Methoden).

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

    Die Verwendung von SecureMethod auf Klassenebene ermöglicht es allen Methoden auf allen Schnittstellen in der Klasse, mit Rollen im COM+-Katalog administrativ konfiguriert zu werden. Wenn die Klasse jeweils zwei Schnittstellen mit demselben Methodennamen und -rollen implementiert, müssen die Rollen auf beiden Methoden konfiguriert werden, wie sie im COM+-Katalog angezeigt werden (es sei denn, die Klasse implementiert die spezifische Methode, z. B. IFoo.Method1). Wenn das SecurityRole-Attribut jedoch für die Klassenmethode verwendet wird, werden alle Methoden mit demselben Methodennamen automatisch mit dieser Rolle konfiguriert, wenn die Assembly registriert wird.

  • Das SecureMethod-Attribut kann auch auf der Methodenebene platziert werden.

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

    Im Beispiel werden IFoo und beide Methoden im COM+-Katalog angezeigt und daher können Rollen in beiden Methoden administrativ konfiguriert werden, jedoch wird die Methodenebene RBS nur auf Methode1 erzwungen. Verwenden Sie SecureMethod oder SecurityRole für alle Methoden, die erforderlich sind, um an der RBS-Sicherheit der Methode teilzunehmen oder SecureMethod auf der Klassenebene zu platzieren, wie zuvor beschrieben.

Wenn RBS auf Der Methodenebene konfiguriert ist, ist die Marshaller-Rolle erforderlich: Wenn Methodenaufrufe ausgeführt werden und RBS nicht für Methoden konfiguriert ist, führt die Serviced-Komponenteninfrastruktur Aufrufe auf IRemoteDispatch aus. Wenn Methodenaufrufe ausgeführt werden und RBS für Methoden konfiguriert ist (wenn das SecureMethod-Attribut vorhanden ist), wird der Methodenaufruf mithilfe von DCOM mithilfe der schnittstelle durchgeführt, die der Methode zugeordnet ist. Daher wird DCOM garantieren, dass RBS auf Der Methodenebene erzwungen wird. Wie in den Abschnitten "Aktivierung" und "Abfangen" beschrieben, führt COM-Interop und rsCP dann Aufrufe für IManagedObject aus (um Remoteaktivatoren den Verweis in ihren Raum zu verordnen) und IServicedComponentInfo (um das Remoteobjekt abzufragen). Diese Schnittstellen sind dienstbezogenen Komponenten zugeordnet. Da die Komponente so konfiguriert ist, dass Methodenebenenüberprüfungen durchgeführt werden, muss eine Rolle diesen Schnittstellen zugeordnet werden, wenn die Infrastruktur diese Aufrufe erfolgreich ausführen soll.

Daher wird der Anwendung eine Marshaller-Rolle hinzugefügt, wenn die Assembly registriert ist, und Benutzer müssen dieser Rolle dann administratorisch hinzugefügt werden. Am häufigsten werden alle Benutzer der Anwendung dieser Rolle hinzugefügt. Dies unterscheidet sich etwas von nicht verwaltetem COM+, bei dem das Konfigurieren von RBS für Methoden diesen zusätzlichen Konfigurationsschritt nicht erfordert. Das automatische Hinzufügen von "Jeder" zu dieser Rolle während der Registrierung ist ein potenzielles Sicherheitsloch, da jeder jetzt Komponenten aktivieren (aber nicht aufrufen) kann, wo sie möglicherweise nicht über die Rechte zum Aktivieren verfügen. Die Marshaller-Rolle wird auch der IDisposable-Schnittstelle hinzugefügt, damit Clients das Objekt löschen können. Eine Alternative zur Marshaller-Rolle besteht darin, benutzern die relevanten Rollen zu den drei genannten Schnittstellen hinzuzufügen.

Remotekomponenten

Die Klasse ServicedComponent enthält MarshalByRefObject in seiner Vererbungsstruktur und kann daher von Remoteclients aus darauf zugegriffen werden. Es gibt viele Varianten, wie Dienstkomponenten remote verfügbar gemacht werden. Auf Dienstkomponenten kann remote zugegriffen werden:

  • Der HTTP-Kanal mit dienstierten Komponenten, die in ASP.NET aufgerufen werden, bietet gute Sicherheits- und Verschlüsselungsoptionen sowie bekannte Skalierbarkeit und Leistung. Bei Verwendung mit SOAP sind weitere Interoperabilitätsoptionen vorhanden. Dienstfähige Komponenten können in IIS/ASP.NET als COM+-Bibliotheksanwendung gehostet werden. Wenn eine COM+-Serveranwendung verwendet wird, kann der IIS/ASP.NET-Host mithilfe von DCOM auf die Komponenten zugreifen.
  • Eine alternative Möglichkeit zum Verfügbarmachen einer dienstierten Komponente als SOAP-Endpunkt wird in COM+ Webdiensten erläutert: Die Check-Box Route zu XML-Webdiensten.
  • DCOM, wenn dienstfähige Komponenten in Dllhost gehostet werden. Diese Option bietet optimale Leistung und Sicherheit und die Möglichkeit, Dienstkontexte auf computerübergreifendem Computer zu übergeben. Die Hauptentwurfsfrage beim Auswählen einer Remotingtechnologie sollte sein, ob die Dienste über Computer hinweg fließen müssen. Beispielsweise ist DCOM innerhalb einer Serverfarm, auf der eine Transaktion auf einem Computer erstellt wird, und es ist erforderlich, dass die Transaktion auf einem anderen Computer fortgesetzt werden kann, DCOM ist das einzige Protokoll, das verwendet werden kann, um dies zu erreichen. Wenn Clients jedoch einfach einen Remote-ServicedComponent aufrufen müssen, sind die HTTP-Kanal- oder SOAP-Endpunktansätze gute Alternativen.
  • Ein .NET-Remotingkanal (z. B. ein TCP- oder benutzerdefinierter Kanal). Um den TCP-Kanal zu verwenden, benötigen Sie eine Prozesshörung auf einem Socket. Im Allgemeinen wird ein benutzerdefinierter Prozess verwendet, um auf einen Socket zu lauschen und dann dienstierte Komponenten entweder als COM+-Bibliothek oder Serveranwendung zu hosten. Alternativ kann Dllhost als Listener verwendet werden. Beide Ansätze werden am wenigsten verwendet und erfordern das Schreiben eines benutzerdefinierten Socketlisteners mit bewährter Leistung, Skalierbarkeit und Sicherheit. Daher sind die ASP.NET- oder DCOM-Lösungen die besten Ansätze für die meisten Projekte.

Um remote über DCOM auf eine dienstierte Komponente zuzugreifen und in Dllhost gehostet zu werden, stellen Sie zuerst sicher, dass die Assembly in einer COM+-Serveranwendung registriert ist und auf dem Servercomputer in das GAC platziert wird. Verwenden Sie dann die COM+-Anwendungsexportfunktion, um eine MSI-Datei für einen Anwendungsproxy zu erstellen. Installieren Sie den Anwendungsproxy auf dem Client. Eingebettet in den Anwendungsproxy ist die verwaltete Assembly. Das Installationsprogramm registriert auch die Assembly und platziert sie im GAC auf dem Clientcomputer. Deshalb gilt Folgendes:

  • Die .NET Framework ist erforderlich, um auf dem Client und dem Server installiert zu werden. Dies ist auf dem Clientcomputer erforderlich, auch wenn nur nicht verwaltete Clients auf Remotedienstkomponenten zugreifen. Auf Windows 2000-Plattformen ist auch Service Pack 3 erforderlich.
  • Nach der Deinstallation des Proxys muss die Assembly auch aus dem GAC entfernt werden.

Die Infrastruktur, nachdem die Serverkomponente in verwaltetem Code von der Clientseite aktiviert wurde, wird in Abbildung 6 dargestellt.

Die Verwendung von DCOM impliziert, dass die CLR in Dllhost gehostet wird. Dies bedeutet, dass sich die Anwendungskonfigurationsdatei dllhost.exe.config im System32-Verzeichnis befindet. Dies bedeutet auch, dass die Konfigurationsdatei für alle Dllhost-Prozesse auf dem Computer gilt. In der nächsten Version der .NET Framework (V1.1) kann das COM+-Anwendungsstammverzeichnis in der COM+-Anwendung festgelegt werden und dieses Verzeichnis wird verwendet, um Konfigurationsdateien und Assemblys für die Anwendung zu ermitteln.

Bei clientaktivierten Objekten wird, wenn der URI eines Objekts angefordert wird, eine Lebensdauer-Lease für dieses Objekt erstellt. Wie weiter oben im Abschnitt "Aktivierung" beschrieben, wird ein URI vom Remotedienstanbieter-Komponentenproxy angefordert. Dies kann auch auftreten, wenn eine vorhandene In-Process-Serviced-Komponente an einen Remoteprozess gemarstet wird – ein URI wird angefordert, wenn ein MBR-Objekt von .NET außerhalb einer Anwendungsdomäne ge marshaliert wird. Der URI wird verwendet, um sicherzustellen, dass Objektidentitäten in .NET eindeutig sind und die Proxykette verhindert wird. Wenn ein verwalteter Client eine Remotedienstkomponente aktiviert, werden die Leasezeiten daher auf dem Serverobjekt verwendet. Beachten Sie, dass nicht verwaltete Clients keinen Remotekomponentenproxy auf der Clientseite haben und daher den URI des Objekts nicht anfordern. Stattdessen verwenden nicht verwaltete Clients DCOM, um die Objektidentität sicherzustellen. Aus diesem Grund werden Die Leasingzeiten für serviced-Komponenten nicht verwendet, wenn sie von nicht verwalteten Clients aktiviert werden.

Wenn Die Leasingzeiten mit dienstierten Komponenten verbunden sind, empfiehlt es sich, die InitialLeaseTime- und RenewOnCallTime-Timeoutwerte auf einen kleinen Wert festzulegen, möglicherweise sogar so klein wie 10 Sekunden. Serviced-Komponenten werden entweder mithilfe von Dispose() zerstört oder der GC die Objekte bereinigen lassen. Wenn Dispose() aufgerufen wird, gibt der Remotedienstkomponentenproxy den Verweis auf den DCOM-Proxy frei und stellt sich dann dem nächsten GC zur Verfügung. Das Serverobjekt verarbeitet den Dispose-Aufruf (oder ein neues Serverobjekt wird erstellt, um den Remoteaufruf an Dispose()zu nutzen), den zugehörigen COM+-Kontext zu zerstören und sich dann für den nächsten GC verfügbar zu machen, aber nur, wenn die Leasezeit zeit abgelaufen ist. Wenn der Client "Dispose(" nicht aufruft), muss der Server zunächst warten, bis der Clientseitige GC den Verweis auf den DCOM-Proxy freigibt und sich dann selbst und der COM+-Kontext dem nächsten GC zur Verfügung stellen muss, nachdem die Leasingzeit abgelaufen ist. Rufen Sie daher "Dispose()" auf und verringern Sie zusätzlich die Standard-Leasingzeit. Auch wenn der Client lebendig bleibt und die Leasingzeit abläuft, behält der DCOM-Verweis auf das Serverobjekt das Serverobjekt am Leben. Der DCOM-Verweis wird jedoch nicht immer verwendet, um die dienstierte Komponente lebendig zu halten. Wenn der Client über einen CLR-Remotingkanal oder COM+ SOAP-Dienste auf das Objekt zugreift, bleibt nur der starke Verweis, der auf die Lease zurückzuführen ist, die dienstierte Komponente lebendig.

Zusammenfassung

In diesem Artikel wurden nur einige der Dienste erläutert, die für verwalteten Code verfügbar sind. Alle COM+-Dienste sind für verwalteten Code verfügbar, z. B. Transaktionsisolationsstufen, Prozessinitialisierung, Dienste ohne Komponenten und Prozessrecycling. Die .NET Framework bietet jetzt den gleichen Zugriff auf alle COM+-Dienste auf konsistente und logische Weise. Darüber hinaus sind eine Reihe innovativer Teile der .NET Framework, wie z. B. ASP.NET, Microsoft ADO.NET und Messaging, tief in .NET Enterprise Services integriert und nutzen Dienste wie Transaktionen und Objektpooling. Diese Integration bietet ein konsistentes Architektur- und Programmiermodell. Der System.EnterpriseServices-Namespace stellt das Programmiermodell bereit, um verwalteten Klassen Dienste hinzuzufügen.