Freigeben über


Bereitstellen eines asynchronen Visual Studio-Diensts

Wenn Sie einen Dienst abrufen möchten, ohne den UI-Thread zu blockieren, sollten Sie einen asynchronen Dienst erstellen und das Paket in einem Hintergrundthread laden. Zu diesem Zweck können Sie einen anstelle eines AsyncPackage Package, und den Dienst mit den speziellen asynchronen Methoden des asynchronen Pakets hinzufügen.

Informationen zum Bereitstellen synchroner Visual Studio-Dienste finden Sie unter How to: Provide a service.

Implementieren eines asynchronen Diensts

  1. Erstellen Sie ein VSIX-Projekt (File>New>Project>Visual C#>Extensiblity>VSIX Project). Nennen Sie das Projekt TestAsync.

  2. Fügen Sie dem Projekt ein VSPackage hinzu. Wählen Sie den Projektknoten im Projektmappen-Explorer aus, und klicken Sie auf "Visual C#-Elementerweiterung>>visual Studio-Paket hinzufügen>>". Nennen Sie diese Datei TestAsyncPackage.cs.

  3. Ändern Sie in TestAsyncPackage.cs das Paket so, dass es von statt Packagevon AsyncPackage :

    public sealed class TestAsyncPackage : AsyncPackage
    
  4. Um einen Dienst zu implementieren, müssen Sie drei Typen erstellen:

    • Eine Schnittstelle, die den Dienst identifiziert. Viele dieser Schnittstellen sind leer, d. h. sie haben keine Methoden, da sie nur zum Abfragen des Diensts verwendet werden.

    • Eine Schnittstelle, die die Dienstschnittstelle beschreibt. Diese Schnittstelle enthält die zu implementierenden Methoden.

    • Eine Klasse, die sowohl den Dienst als auch die Dienstschnittstelle implementiert.

  5. Das folgende Beispiel zeigt eine sehr einfache Implementierung der drei Typen. Der Konstruktor der Dienstklasse muss den Dienstanbieter festlegen. In diesem Beispiel fügen wir den Dienst einfach der Paketcodedatei hinzu.

  6. Fügen Sie der Paketdatei die folgenden Direktiven hinzu:

    using System.Threading;
    using System.Threading.Tasks;
    using System.Runtime.CompilerServices;
    using System.IO;
    using Microsoft.VisualStudio.Threading;
    using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider;
    using Task = System.Threading.Tasks.Task;
    
  7. Dies ist die asynchrone Dienstimplementierung. Beachten Sie, dass Sie den asynchronen Dienstanbieter anstelle des synchronen Dienstanbieters im Konstruktor festlegen müssen:

    public class TextWriterService : STextWriterService, ITextWriterService
    {
        private IAsyncServiceProvider asyncServiceProvider;
    
        public TextWriterService(IAsyncServiceProvider provider)
        {
            // constructor should only be used for simple initialization
            // any usage of Visual Studio service, expensive background operations should happen in the
            // asynchronous InitializeAsync method for best performance
            asyncServiceProvider = provider;
        }
    
        public async Task InitializeAsync(CancellationToken cancellationToken)
        {
            await TaskScheduler.Default;
            // do background operations that involve IO or other async methods
    
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
            // query Visual Studio services on main thread unless they are documented as free threaded explicitly.
            // The reason for this is the final cast to service interface (such as IVsShell) may involve COM operations to add/release references.
    
            IVsShell vsShell = this.asyncServiceProvider.GetServiceAsync(typeof(SVsShell)) as IVsShell;
            // use Visual Studio services to continue initialization
        }
    
        public async Task WriteLineAsync(string path, string line)
        {
            StreamWriter writer = new StreamWriter(path);
            await writer.WriteLineAsync(line);
            writer.Close();
        }
    }
    
    public interface STextWriterService
    {
    }
    
    public interface ITextWriterService
    {
        System.Threading.Tasks.Task WriteLineAsync(string path, string line);
    }
    

Registrieren eines Diensts

Um einen Dienst zu registrieren, fügen Sie das ProvideServiceAttribute Paket hinzu, das den Dienst bereitstellt. Anders als beim Registrieren eines synchronen Diensts müssen Sie sicherstellen, dass paket- und dienstseitiges Laden asynchrones Laden unterstützt:

  • Sie müssen das Feld "AllowsBackgroundLoading = true " hinzufügen PackageRegistrationAttribute , um sicherzustellen, dass das Paket asynchron initialisiert werden kann. Weitere Informationen zum PackageRegistrationAttribute finden Sie unter Registrieren und Aufheben der Registrierung von VSPackages.

  • Sie müssen das Feld IsAsyncQueryable = true hinzufügen, ProvideServiceAttribute um sicherzustellen, dass die Dienstinstanz asynchron initialisiert werden kann.

    Nachfolgend sehen Sie ein Beispiel für eine AsyncPackage asynchrone Dienstregistrierung:

[ProvideService((typeof(STextWriterService)), IsAsyncQueryable = true)]
[ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(TestAsyncPackage.PackageGuidString)]
public sealed class TestAsyncPackage : AsyncPackage
{. . . }

Hinzufügen eines Diensts

  1. Entfernen Sie in TestAsyncPackage.cs die Initialize() Methode, und überschreiben Sie die InitializeAsync() Methode. Fügen Sie den Dienst hinzu, und fügen Sie eine Rückrufmethode zum Erstellen der Dienste hinzu. Nachfolgend sehen Sie ein Beispiel für den asynchronen Initialisierer, der einen Dienst hinzufügt:

    protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        await base.InitializeAsync(cancellationToken, progress);
        this.AddService(typeof(STextWriterService), CreateTextWriterService);
    }
    
    

    Um diesen Dienst außerhalb dieses Pakets sichtbar zu machen, legen Sie den Heraufstufen-Flagwert auf "true " als letzten Parameter fest: this.AddService(typeof(STextWriterService), CreateTextWriterService, true);

  2. Fügen Sie einen Verweis auf Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll hinzu.

  3. Implementieren Sie die Rückrufmethode als asynchrone Methode, die den Dienst erstellt und zurückgibt.

    public async Task<object> CreateTextWriterService(IAsyncServiceContainer container, CancellationToken cancellationToken, Type serviceType)
    {
        TextWriterService service = new TextWriterService(this);
        await service.InitializeAsync(cancellationToken);
        return service;
    }
    
    

Verwenden eines Diensts

Jetzt können Sie den Dienst abrufen und seine Methoden verwenden.

  1. Dies wird im Initialisierer angezeigt, aber Sie können den Dienst überall abrufen, wo Sie den Dienst verwenden möchten.

    protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        await base.InitializeAsync(cancellationToken, progress);
        this.AddService(typeof(STextWriterService), CreateTextWriterService);
    
        ITextWriterService textService = await this.GetServiceAsync(typeof(STextWriterService)) as ITextWriterService;
        string userpath = @"C:\MyDir\MyFile.txt";
        await textService.WriteLineAsync(userpath, "this is a test");
    }
    
    

    Vergessen Sie nicht, in einen Dateinamen und Pfad zu wechseln userpath , der auf Ihrem Computer sinnvoll ist!

  2. Erstellen Sie den Code, und führen Sie den Code aus. Wenn die experimentelle Instanz von Visual Studio angezeigt wird, öffnen Sie eine Lösung. Dadurch wird das AsyncPackage automatische Laden verursacht. Wenn der Initialisierer ausgeführt wurde, sollten Sie eine Datei an dem von Ihnen angegebenen Speicherort finden.

Verwenden eines asynchronen Diensts in einem Befehlshandler

Hier ist ein Beispiel für die Verwendung eines asynchronen Diensts in einem Menübefehl. Sie können das hier gezeigte Verfahren verwenden, um den Dienst in anderen nicht asynchronen Methoden zu verwenden.

  1. Fügen Sie Ihrem Projekt einen Menübefehl hinzu. (Im Projektmappen-Explorer wählen Sie den Projektknoten aus, klicken Sie mit der rechten Maustaste, und wählen Sie "Benutzerdefinierten Befehl hinzufügen>">>aus. Nennen Sie die Befehlsdatei TestAsyncCommand.cs.

  2. Die benutzerdefinierte Befehlsvorlage fügt die Methode der Initialize() Datei TestAsyncPackage.cs erneut hinzu, um den Befehl zu initialisieren. Kopieren Sie in der Initialize() Methode die Zeile, die den Befehl initialisiert. Diese sollte wie folgt aussehen:

    TestAsyncCommand.Initialize(this);
    

    Verschieben Sie diese Zeile in die InitializeAsync() Methode in der Datei "AsyncPackageForService.cs ". Da sich dies in einer asynchronen Initialisierung befindet, müssen Sie zum Standard Thread wechseln, bevor Sie den Befehl mithilfe SwitchToMainThreadAsyncinitialisieren. Dies sollte jetzt wie folgt aussehen:

    
    protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        await base.InitializeAsync(cancellationToken, progress);
        this.AddService(typeof(STextWriterService), CreateTextWriterService);
    
        ITextWriterService textService =
           await this.GetServiceAsync(typeof(STextWriterService)) as ITextWriterService;
    
        string userpath = @"C:\MyDir\MyFile.txt";
        await textService.WriteLineAsync(userpath, "this is a test");
    
        await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
        TestAsyncCommand.Initialize(this);
    }
    
    
  3. Löschen Sie die Initialize() Methode.

  4. Suchen Sie in der Datei TestAsyncCommand.cs die MenuItemCallback() Methode. Löschen Sie den Textkörper der Methode.

  5. Fügen Sie eine using-Direktive hinzu:

    using System.IO;
    
  6. Fügen Sie eine asynchrone Methode namens hinzu UseTextWriterAsync(), die den Dienst abruft und seine Methoden verwendet:

    private async System.Threading.Tasks.Task UseTextWriterAsync()
    {
        // Query text writer service asynchronously to avoid a blocking call.
        ITextWriterService textService =
           await AsyncServiceProvider.GlobalProvider.GetServiceAsync(typeof(STextWriterService))
              as ITextWriterService;
    
        string userpath = @"C:\MyDir\MyFile.txt";
        await textService.WriteLineAsync(userpath, "this is a test");
       }
    
    
  7. Rufen Sie diese Methode aus der MenuItemCallback() Methode auf:

    private void MenuItemCallback(object sender, EventArgs e)
    {
        UseTextWriterAsync();
    }
    
    
  8. Erstellen Sie die Projektmappe, und beginnen Sie mit dem Debuggen. Wenn die experimentelle Instanz von Visual Studio angezeigt wird, wechseln Sie zum Menü "Extras ", und suchen Sie nach dem Menüelement "Invoke TestAsyncCommand" . Wenn Sie darauf klicken, schreibt der TextWriterService in die von Ihnen angegebene Datei. (Sie müssen keine Lösung öffnen, da das Aufrufen des Befehls auch dazu führt, dass das Paket geladen wird.)