Freigeben über


Nutzen eines brokerierten Diensts

In diesem Dokument werden alle Code, Muster und Vorsichtsmaßnahmen beschrieben, die für den Erwerb, die allgemeine Nutzung und die Entsorgung eines vermittelten Dienstes relevant sind. Wenn Sie lernen möchten, einen bestimmten brokerierten Dienst nach dem Erwerb zu verwenden , suchen Sie nach der jeweiligen Dokumentation für diesen brokerierten Dienst.

Bei allen Code in diesem Dokument wird dringend empfohlen, das Feature für nullwerte Referenztypen von C# zu aktivieren.

Abrufen eines IServiceBroker-Elements

Um einen brokerierten Dienst zu erwerben, müssen Sie zuerst eine Instanz von IServiceBroker. Wenn Ihr Code im Kontext von MEF oder einem VS-Paket ausgeführt wird, möchten Sie normalerweise den globalen Dienstbroker.

Brokered Services selbst sollten die IServiceBroker zugewiesenen Dienste verwenden, wenn ihre Dienstfactory aufgerufen wird.

Der globale Servicebroker

Visual Studio bietet zwei Möglichkeiten, den globalen Dienstbroker zu erwerben.

Verwenden Sie GlobalProvider.GetServiceAsync , um folgendes SVsBrokeredServiceContaineranzufordern:

IBrokeredServiceContainer container = await AsyncServiceProvider.GlobalProvider.GetServiceAsync<SVsBrokeredServiceContainer, IBrokeredServiceContainer>();
IServiceBroker serviceBroker = container.GetFullAccessServiceBroker();

Ab Visual Studio 2022 kann Code, der in einer aktivierten MEF-Erweiterung ausgeführt wird, den globalen Dienstbroker importieren:

[Import(typeof(SVsFullAccessServiceBroker))]
IServiceBroker ServiceBroker { get; set; }

Beachten Sie das typeof Argument für das Import-Attribut, das erforderlich ist.

Jede Anforderung für die Globale IServiceBroker erzeugt eine neue Instanz eines Objekts, das als Ansicht in den globalen Broker-Dienstcontainer dient. Mit dieser eindeutigen Instanz des Dienstbrokers kann Ihr Client Ereignisse empfangen AvailabilityChanged , die für die Verwendung dieses Clients eindeutig sind. Es wird empfohlen, dass jeder Client/jede Klasse in Ihrer Erweiterung einen eigenen Servicebroker unter Verwendung einer der oben genannten Ansätze erhält, anstatt eine Instanz zu erwerben und sie über ihre gesamte Erweiterung zu teilen. Dieses Muster fördert auch sichere Codierungsmuster, bei denen ein brokerter Dienst nicht den globalen Dienstbroker verwenden sollte.

Wichtig

Implementierungen von in der IServiceBroker Regel nicht implementieren IDisposable, aber diese Objekte können nicht erfasst werden, während AvailabilityChanged Handler vorhanden sind. Achten Sie darauf, das Hinzufügen/Entfernen von Ereignishandlern auszugleichen, insbesondere, wenn sich der Code während der Lebensdauer des Prozesses nicht Karte dem Dienstbroker.

Kontextspezifische Dienstbroker

Die Verwendung des entsprechenden Servicebrokers ist eine wichtige Anforderung des Sicherheitsmodells von brokerierten Diensten, insbesondere im Kontext von Live-Freigabesitzungen.

Brokered Services werden mit ihren eigenen IServiceBroker aktiviert und sollten diese Instanz für jeden ihrer eigenen Broker-Service-Anforderungen verwenden, einschließlich der Dienste, die Proffermit ihnen vertraut sind. Dieser Code stellt einen BrokeredServiceFactory Dienstbroker bereit, der vom instanziierten brokerierten Dienst verwendet werden soll.

Abrufen eines Brokerdienstproxys

Der Abruf eines brokerierten Diensts erfolgt in der Regel mit der GetProxyAsync Methode.

Für die GetProxyAsync Methode ist eine ServiceRpcDescriptor und die Dienstschnittstelle als generisches Typargument erforderlich. Die Dokumentation für den angeforderten brokerierten Dienst sollte angeben, wo der Deskriptor abgerufen werden soll und welche Schnittstelle verwendet werden soll. Für brokerierte Dienste, die in Visual Studio enthalten sind, sollte die zu verwendende Schnittstelle in der IntelliSense-Dokumentation für den Deskriptor angezeigt werden. Erfahren Sie, wie Sie Deskriptoren für brokerierte Dienste in Visual Studio finden, um verfügbare Brokered Services zu ermitteln.

IServiceBroker broker; // Acquired as described earlier in this topic
IMyService? myService = await broker.GetProxyAsync<IMyService>(serviceDescriptor, cancellationToken);
using (myService as IDisposable)
{
    Assumes.Present(myService); // Throw if service was not available
    await myService.SayHelloAsync();
}

Wie bei allen brokerierten Dienstanforderungen aktiviert der obige Code eine neue Instanz eines brokerierten Diensts. Nach der Verwendung des Diensts verworfen der obige Code den Proxy, da die Ausführung den using Block beendet.

Wichtig

Jeder abgerufene Proxy muss gelöscht werden, auch wenn die Dienstschnittstelle nicht von IDisposable. Die Entsorgung ist wichtig, da der Proxy häufig E/A-Ressourcen unterstützt, die verhindern, dass er garbage collection wird. Die Entsorgung beendet die E/A, sodass der Proxy garbage collection werden kann. Verwenden Sie eine bedingte Umwandlung, um IDisposable sie zur Entsorgung zu verwenden und für die Umwandlung vorzubereiten, um eine Ausnahme für null Proxys oder Proxys zu vermeiden, die nicht tatsächlich implementiert IDisposablewerden.

Achten Sie darauf, das neueste Microsoft.ServiceHub.Analyzers NuGet-Paket zu installieren und die ISBxxxx-Analyseregeln zu aktivieren, um solche Lecks zu verhindern.

Die Entsorgung des Proxys führt zu der Entsorgung des brokerierten Diensts, der diesem Client zugeordnet war.

Wenn Ihr Code einen brokerierten Dienst erfordert und seine Arbeit nicht abschließen kann, wenn der Dienst nicht verfügbar ist, sollten Sie dem Benutzer möglicherweise ein Fehlerdialogfeld anzeigen, wenn der Code die Benutzeroberfläche besitzt, anstatt eine Ausnahme auszuwerfen.

Client-RPC-Ziele

Einige brokerierte Dienste akzeptieren oder erfordern ein Client-RPC-Ziel für "Rückrufe". Eine solche Option oder Anforderung sollte in der Dokumentation dieses bestimmten brokerierten Dienstes enthalten sein. Für visual Studio brokered Services sollten diese Informationen in der IntelliSense-Dokumentation zum Deskriptor enthalten sein.

In diesem Fall kann ein Client eine der folgenden Verwendungen ServiceActivationOptions.ClientRpcTarget bereitstellen:

IMyService? myService = await broker.GetProxyAsync<IMyService>(
    serviceDescriptor,
    new ServiceActivationOptions
    {
        ClientRpcTarget = new MyCallbackObject(),
    },
    cancellationToken);

Aufrufen des Clientproxys

Das Ergebnis des Anforderns eines brokerierten Diensts ist eine Instanz der Dienstschnittstelle, die von einem Proxy implementiert wird. Dieser Proxy leitet Aufrufe und Ereignisse in jeder Richtung weiter, wobei einige wichtige Verhaltensunterschiede von dem auftreten, was man beim direkten Aufrufen des Diensts erwarten kann.

Observer-Muster

Wenn der Dienstvertrag Parameter vom Typ IObserver<T>akzeptiert, erfahren Sie möglicherweise mehr über das Erstellen eines solchen Typs unter "Implementieren eines Beobachters".

Mit ActionBlock<TInput> der AsObserver Erweiterungsmethode lässt sich eine Anpassung vornehmenIObserver<T>. Die System.Reactive.Observer-Klasse aus dem Reaktiven Framework ist eine weitere Alternative zur Implementierung der Schnittstelle selbst.

Vom Proxy ausgelöste Ausnahmen

  • Erwarten Sie RemoteInvocationException , dass sie für jede Ausnahme ausgelöst wird, die vom brokerierten Dienst ausgelöst wird. Die ursprüngliche Ausnahme finden Sie in der InnerException. Dies ist ein natürliches Verhalten für einen remote gehosteten Dienst, da es sich um ein Verhalten handelt JsonRpc. Wenn der Dienst lokal ist, schließt der lokale Proxy alle Ausnahmen auf die gleiche Weise um, sodass der Clientcode nur einen Ausnahmepfad aufweisen kann, der für lokale und Remotedienste funktioniert.
    • Überprüfen Sie die ErrorCode Eigenschaft, wenn in der Dienstdokumentation vorgeschlagen wird, dass bestimmte Codes basierend auf bestimmten Bedingungen festgelegt werden, auf denen Sie verzweigen können.
    • Eine breitere Reihe von Fehlern wird durch abfangen RemoteRpcException, was der Basistyp für die RemoteInvocationException.
  • Erwarten Sie ConnectionLostException , dass sie von jedem Anruf ausgelöst werden, wenn die Verbindung mit einem Remotedienst abstürzt oder der Prozess, bei dem der Dienst abstürzt. Dies ist in erster Linie bedenklich, wenn der Dienst remote erworben werden kann.

Zwischenspeichern des Proxys

Es gibt einige Kosten bei der Aktivierung eines brokerierten Diensts und eines zugeordneten Proxys, insbesondere wenn der Dienst von einem Remoteprozess stammt. Wenn die häufige Verwendung eines brokerierten Diensts die Zwischenspeicherung des Proxys über viele Aufrufe in einer Klasse garantiert, kann der Proxy in einem Feld in dieser Klasse gespeichert werden. Die enthaltende Klasse sollte einweg sein und den Proxy innerhalb seiner Dispose Methode verwerfen. Betrachten Sie das folgende Beispiel:

class MyExtension : IDisposable
{
    readonly IServiceBroker serviceBroker;
    IMyService? serviceProxy;

    internal MyExtension(IServiceBroker serviceBroker)
    {
        this.serviceBroker = serviceBroker;
    }

    async Task SayHiAsync(CancellationToken cancellationToken)
    {
        if (this.serviceProxy is null)
        {
            this.serviceProxy = await this.serviceBroker.GetProxyAsync<IMyService>(serviceDescriptor, cancellationToken);
            Assumes.Present(this.serviceProxy);
        }

        await this.serviceProxy.SayHelloAsync();
    }

    public void Dispose()
    {
        (this.serviceProxy as IDisposable)?.Dispose();
    }
}

Der obige Code ist grob korrekt, berücksichtigt jedoch nicht die Rennbedingungen zwischen Dispose und SayHiAsync. Der Code berücksichtigt AvailabilityChanged auch keine Ereignisse, die zur Entsorgung des zuvor erworbenen Proxys und zur erneuten Autorisierung des Proxys führen sollten, wenn er das nächste Mal erforderlich ist.

Die ServiceBrokerClient Klasse wurde entwickelt, um diese Race- und Invalidationsbedingungen zu behandeln, um Ihren eigenen Code einfach zu halten. Betrachten Sie dieses aktualisierte Beispiel, das den Proxy mithilfe dieser Hilfsklasse zwischenspeichert:

class MyExtension : IDisposable
{
    readonly ServiceBrokerClient serviceBrokerClient;

    internal MyExtension(IServiceBroker serviceBroker)
    {
        this.serviceBrokerClient = new ServiceBrokerClient(serviceBroker);
    }

    async Task SayHiAsync(CancellationToken cancellationToken)
    {
        using var rental = await this.serviceBrokerClient.GetProxyAsync<IMyService>(descriptor, cancellationToken);
        Assumes.Present(rental.Proxy); // Throw if service is not available
        IMyService myService = rental.Proxy;
        await myService.SayHelloAsync();
    }

    public void Dispose()
    {
        // Disposing the ServiceBrokerClient will dispose of all proxies
        // when their rentals are released.
        this.serviceBrokerClient.Dispose();
    }
}

Der oben genannte Code ist weiterhin dafür verantwortlich, die ServiceBrokerClient und jede Vermietung eines Proxys zu verwerfen. Race conditions between disposal and use of the proxy are handled by the ServiceBrokerClient object, which will dispose of each cached proxy at the time of its own disposal or when the last rental of that proxy has been released, whichever comes last.

Wichtige Vorbehalte in Bezug auf die ServiceBrokerClient

  • ServiceBrokerClient indiziert die zwischengespeicherten Proxys basierend auf dem ServiceMoniker einzigen. Wenn Sie übergeben ServiceActivationOptions und ein zwischengespeicherter Proxy bereits verfügbar ist, wird der zwischengespeicherte Proxy zurückgegeben, ohne das ServiceActivationOptionsunerwartete Verhalten des Diensts zu verwenden. Erwägen Sie die direkte Verwendung IServiceBroker in solchen Fällen.

  • Speichern Sie die abgerufenen ServiceBrokerClient.GetProxyAsync Daten ServiceBrokerClient.Rental<T> nicht in einem Feld. Der Proxy wird bereits außerhalb des Bereichs einer Methode durch die ServiceBrokerClient. Wenn Sie mehr Kontrolle über die Lebensdauer des Proxys benötigen, insbesondere um die Neuakquisition aufgrund eines AvailabilityChanged Ereignisses, verwenden Sie IServiceBroker stattdessen direkt, und speichern Sie den Dienstproxy in einem Feld.

  • Erstellen und Speichern ServiceBrokerClient in einem Feld und nicht in einer lokalen Variablen. Wenn Sie sie als lokale Variable in einer Methode erstellen und verwenden, wird der Wert IServiceBroker nicht direkt hinzugefügt, sondern sie müssen jetzt zwei Objekte (der Kunde und die Vermietung) anstelle eines (der Dienst) löschen.

Auswählen zwischen IServiceBroker und ServiceBrokerClient

Beide sind benutzerfreundlich, und der Standardwert sollte wahrscheinlich sein IServiceBroker.

Kategorie IServiceBroker ServiceBrokerClient
Benutzerfreundliche Ja Ja
Entsorgung erforderlich Nein Ja
Verwaltet die Lebensdauer des Proxys Nein Der Besitzer muss den Proxy löschen, wenn er mit der Verwendung fertig ist. Ja, sie werden lebendig gehalten und wiederverwendet, solange sie gültig sind.
Anwendbar für zustandslose Dienste Ja Ja
Anwendbar für zustandsbehaftete Dienste Ja Nein
Geeignet, wenn Ereignishandler dem Proxy hinzugefügt werden Ja Nein
Ereignis, das benachrichtigt werden soll, wenn der alte Proxy ungültig ist AvailabilityChanged Invalidated

ServiceBrokerClient bietet eine bequeme Möglichkeit für Sie, schnelle und häufige Wiederverwendung eines Proxys zu erhalten, bei dem Sie sich nicht darum kümmern, ob der zugrunde liegende Dienst von unter Ihnen zwischen Vorgängen auf oberster Ebene geändert wird.

Wenn Sie sich jedoch um diese Dinge kümmern und die Lebensdauer Ihrer Proxys selbst verwalten möchten, oder Sie Ereignishandler benötigen (was bedeutet, dass Sie die Lebensdauer des Proxys verwalten müssen), sollten Sie verwenden IServiceBroker.

Ausfallsicherheit von Dienstunterbrechungen

Es gibt einige Arten von Dienstunterbrechungen, die mit vermittelten Diensten möglich sind:

Fehler bei der Aktivierung durch brokerierte Dienste

Wenn eine brokerierte Serviceanfrage von einem verfügbaren Dienst erfüllt werden kann, aber die Dienstfactory eine unbehandelte Ausnahme auslöst, wird eine ServiceActivationFailedException zurück an den Client ausgelöst, damit sie den Fehler des Benutzers verstehen und melden können.

Wenn eine brokerierte Serviceanfrage nicht mit einem verfügbaren Dienst abgeglichen werden kann, null wird sie an den Client zurückgegeben. In einem solchen Fall wird ausgelöst, AvailabilityChanged wann und ob dieser Dienst später verfügbar wird.

Die Serviceanfrage wird möglicherweise nicht abgelehnt, weil der Dienst nicht vorhanden ist, sondern weil die angebotene Version niedriger ist als die angeforderte Version. Ihr Fallbackplan kann das Wiederholen der Serviceanfrage mit niedrigeren Versionen umfassen, die Ihr Client kennt und mit dem sie interagieren kann.

Wenn/wenn die Latenz aller fehlgeschlagenen Versionsüberprüfungen spürbar wird, kann der Client die VisualStudioServices.VS2019_4Services.RemoteBrokeredServiceManifest anfordern, um eine vollständige Vorstellung davon zu erhalten, welche Dienste und Versionen von einer Remotequelle verfügbar sind.

Umgang mit verworfenen Verbindungen

Ein erfolgreich erworbener brokerierter Dienstproxy kann aufgrund einer verworfenen Verbindung oder eines Absturzes im Prozess, in dem er gehostet wird, fehlschlagen. Nach einer solchen Unterbrechung führt jeder Anruf, der an diesem Proxy vorgenommen wurde, dazu ConnectionLostException , dass er ausgelöst wird.

Ein brokered Service Client kann diese Verbindung proaktiv erkennen und darauf reagieren, indem das Disconnected Ereignis behandelt wird. Um dieses Ereignis zu erreichen, muss ein Proxy zum Abrufen des JsonRpc Objekts umwandlungen IJsonRpcClientProxy werden. Diese Umwandlung sollte bedingt erfolgen, damit sie ordnungsgemäß fehlschlägt, wenn der Dienst lokal ist.

if (this.myService is IJsonRpcClientProxy clientProxy)
{
    clientProxy.JsonRpc.Disconnected += JsonRpc_Disconnected;
}

void JsonRpc_Disconnected(object? sender, JsonRpcDisconnectedEventArgs args)
{
    if (args.Reason == DisconnectedReason.RemotePartyTerminated)
    {
        // consider reacquisition of the service.
    }
}

Behandeln von Dienstverfügbarkeitsänderungen

Brokered-Dienstclients erhalten möglicherweise Benachrichtigungen darüber, wann sie eine erneute Abfrage für einen brokerierten Dienst durchführen sollten, für den sie zuvor abgefragt wurden, indem sie das AvailabilityChanged Ereignis behandeln. Handler zu diesem Ereignis sollten hinzugefügt werden, bevor Sie einen brokerierten Dienst anfordern, um sicherzustellen, dass ein Ereignis, das bald ausgelöst wird, nachdem eine Serviceanfrage gestellt wurde, aufgrund einer Racebedingung nicht verloren geht.

Wenn ein Brokerdienst nur für die Dauer der Ausführung einer asynchronen Methode angefordert wird, wird die Behandlung dieses Ereignisses nicht empfohlen. Das Ereignis ist für Clients am relevantesten, die ihren Proxy für längere Zeiträume speichern, sodass sie Dienständerungen ausgleichen müssen und in der Lage sind, ihren Proxy zu aktualisieren.

Dieses Ereignis kann für jeden Thread ausgelöst werden, möglicherweise gleichzeitig mit Code, der einen Dienst verwendet, den das Ereignis beschreibt.

Mehrere Zustandsänderungen können dazu führen, dass dieses Ereignis ausgelöst wird, darunter:

  • Eine Lösung oder ein Ordner, die geöffnet oder geschlossen wird.
  • Eine Livefreigabesitzung wird gestartet.
  • Ein dynamisch registrierter Brokerdienst, der gerade entdeckt wurde.

Ein betroffener Brokerdienst führt nur dazu, dass dieses Ereignis auf Clients ausgelöst wird, die zuvor diesen Dienst angefordert haben, unabhängig davon, ob diese Anforderung erfüllt wurde.

Das Ereignis wird höchstens einmal pro Dienst nach jeder Anforderung für diesen Dienst ausgelöst. Wenn beispielsweise der Client Service A und Dienst B eine Verfügbarkeitsänderung anfordert, wird kein Ereignis an diesen Client ausgelöst. Wenn dienst A eine Verfügbarkeitsänderung erlebt, empfängt der Client das Ereignis. Wenn der Client den Dienst A nicht erneut anfordert, führen nachfolgende Verfügbarkeitsänderungen für A nicht zu weiteren Benachrichtigungen an diesen Client. Sobald der Client A erneut anfordert, kann er die nächste Benachrichtigung zu diesem Dienst erhalten.

Das Ereignis wird ausgelöst, wenn ein Dienst verfügbar ist, nicht mehr verfügbar ist oder eine Implementierungsänderung auftritt, die erfordert, dass alle vorherigen Dienstclients erneut nach dem Dienst abgefragt werden.

Das ServiceBrokerClient behandelt Verfügbarkeitsänderungsereignisse in Bezug auf zwischengespeicherte Proxys automatisch, indem die alten Proxys gelöscht werden, wenn eine Vermietung zurückgegeben und eine neue Instanz des Diensts angefordert wird, wann und ob der Besitzer einen anfordert. Diese Klasse kann Ihren Code erheblich vereinfachen, wenn der Dienst zustandslos ist und nicht erfordert, dass ihr Code Ereignishandler an den Proxy anfügt.

Abrufen einer brokerierten Dienstpipeline

Obwohl der Zugriff auf einen brokerierten Dienst über einen Proxy die am häufigsten verwendete und bequeme Technik ist, kann es in erweiterten Szenarien vorzuziehen oder erforderlich sein, eine Pipe an diesen Dienst anzufordern, damit der Client den RPC direkt steuern oder einen anderen Datentyp direkt kommunizieren kann.

Eine Leitung an den vermittelten Dienst kann über die GetPipeAsync Methode abgerufen werden. Diese Methode verwendet anstelle ServiceMoniker eines ServiceRpcDescriptor , da die von einem Deskriptor bereitgestellten RPC-Verhaltensweisen nicht erforderlich sind. Wenn Sie einen Deskriptor haben, können Sie den Moniker über die ServiceRpcDescriptor.Moniker Eigenschaft abrufen.

Während Rohre an E/A gebunden sind, sind sie nicht für die Garbage Collection berechtigt. Vermeiden Sie Speicherverluste, indem Sie diese Rohre immer abschließen, wenn sie nicht mehr verwendet werden.

Im folgenden Codeausschnitt wird ein Brokerdienst aktiviert, und der Client verfügt über eine direkte Pipe. Der Client sendet dann den Inhalt einer Datei an den Dienst und trennt sie.

async Task SendMovieAsync(string movieFilePath, CancellationToken cancellationToken)
{
    IServiceBroker serviceBroker;
    IDuplexPipe? pipe = await serviceBroker.GetPipeAsync(serviceMoniker, cancellationToken);
    if (pipe is null)
    {
        throw new InvalidOperationException($"The brokered service '{serviceMoniker}' is not available.");
    }

    try
    {
        // Open the file optimized for async I/O
        using FileStream fs = new FileStream(movieFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);
        await fs.CopyToAsync(pipe.Output.AsStream(), cancellationToken);
    }
    catch (Exception ex)
    {
        // Complete the pipe, passing through the exception so the remote side understands what went wrong.
        await pipe.Input.CompleteAsync(ex);
        await pipe.Output.CompleteAsync(ex);
        throw;
    }
    finally
    {
        // Always complete the pipe after successfully using the service.
        await pipe.Input.CompleteAsync();
        await pipe.Output.CompleteAsync();
    }
}

Testen von brokerierten Dienstclients

Brokered Services sind eine angemessene Abhängigkeit, die beim Testen Ihrer Erweiterung simuliert werden muss. Beim Modellieren eines brokerierten Diensts empfehlen wir die Verwendung eines Modellframeworks, das die Schnittstelle in Ihrem Auftrag implementiert und Code eingibt, den Sie für die spezifischen Member benötigen, die Der Client aufruft. Auf diese Weise können Ihre Tests weiterhin kompiliert und ohne Unterbrechungen ausgeführt werden, wenn Mitglieder der brokerierten Dienstschnittstelle hinzugefügt werden.

Wenn Sie microsoft.VisualStudio.Sdk.TestFramework zum Testen Der Erweiterung verwenden, kann Ihr Test Standardcode enthalten, um einen Simulierten Dienst zu testen, nach dem Der Clientcode abfragen und ausführen kann. Angenommen, Sie wollten den VisualStudioServices.VS2022.FileSystem brokered-Dienst in Ihren Tests modellieren. Sie könnten das Modell mit diesem Code proffern:

IBrokeredServiceContainer sbc = await AsyncServiceProvider.GlobalProvider.GetServiceAsync<SVsBrokeredServiceContainer, IBrokeredServiceContainer>();
Mock<IFileSystem> mockFileSystem = new Mock<IFileSystem>();
sbc.Proffer(VisualStudioServices.VS2022.FileSystem, (ServiceMoniker moniker, ServiceActivationOptions options, IServiceBroker serviceBroker, CancellationToken cancellationToken) => new ValueTask<object?>(mockFileSystem.Object));

Der simulierte Brokerdienstcontainer erfordert nicht, dass zuerst ein proffered-Dienst registriert wird, wie Visual Studio selbst.

Ihr Code unter Test kann den brokerierten Dienst wie gewohnt erwerben, mit der Ausnahme, dass er unter visual Studio unter dem Test ihr Modell anstelle des echten abrufen würde, der unter Visual Studio ausgeführt wird:

IBrokeredServiceContainer sbc = await AsyncServiceProvider.GlobalProvider.GetServiceAsync<SVsBrokeredServiceContainer, IBrokeredServiceContainer>();
IServiceBroker serviceBroker = sbc.GetFullAccessServiceBroker();
IFileSystem? proxy = await serviceBroker.GetProxyAsync<IFileSystem>(VisualStudioServices.VS2022.FileSystem);
using (proxy as IDisposable)
{
    Assumes.Present(proxy);
    await proxy.DeleteAsync(new Uri("file://some/file"), recursive: false, null, this.TimeoutToken);
}