Udostępnij za pośrednictwem


Udostępnianie asynchronicznej usługi Visual Studio

Jeśli chcesz uzyskać usługę bez blokowania wątku interfejsu użytkownika, należy utworzyć usługę asynchroniczną i załadować pakiet w wątku w tle. W tym celu można użyć elementu AsyncPackage , a nie Package, a następnie dodać usługę ze specjalnymi metodami asynchronicznymi pakietu asynchronicznego.

Aby uzyskać informacje na temat udostępniania synchronicznych usług Programu Visual Studio, zobacz How to: Provide a service (Instrukcje: zapewnianie usługi).

Implementowanie usługi asynchronicznej

  1. Utwórz projekt VSIX (Plik>nowy>projekt>Visual C#>Extensiblity>VSIX Project). Nadaj projektowi nazwę TestAsync.

  2. Dodaj pakiet VSPackage do projektu. Wybierz węzeł projektu w Eksplorator rozwiązań i kliknij pozycję Dodaj nowy element Visual C# Items Extensibility Visual Studio Package (Dodaj>nowy element>Visual C# Items>Extensibility>Visual Studio Package). Nadaj temu plikowi nazwę TestAsyncPackage.cs.

  3. W pliku TestAsyncPackage.cs zmień pakiet, aby dziedziczył z AsyncPackage , a nie Package:

    public sealed class TestAsyncPackage : AsyncPackage
    
  4. Aby zaimplementować usługę, należy utworzyć trzy typy:

    • Interfejs identyfikujący usługę. Wiele z tych interfejsów jest pustych, czyli nie ma metod, ponieważ są one używane tylko do wykonywania zapytań dotyczących usługi.

    • Interfejs opisujący interfejs usługi. Ten interfejs zawiera metody do zaimplementowania.

    • Klasa, która implementuje zarówno usługę, jak i interfejs usługi.

  5. W poniższym przykładzie przedstawiono bardzo podstawową implementację trzech typów. Konstruktor klasy usługi musi ustawić dostawcę usług. W tym przykładzie dodamy usługę do pliku kodu pakietu.

  6. Dodaj następujące dyrektywy using do pliku pakietu:

    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. Oto implementacja usługi asynchronicznej. Należy pamiętać, że należy ustawić dostawcę usług asynchronicznych, a nie synchronicznego dostawcę usług w konstruktorze:

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

Rejestrowanie usługi

Aby zarejestrować usługę, dodaj element ProvideServiceAttribute do pakietu, który udostępnia usługę. Inaczej niż w przypadku rejestrowania usługi synchronicznej, musisz upewnić się, że zarówno pakiet, jak i usługa obsługują ładowanie asynchroniczne:

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

Dodawanie usługi

  1. W pliku TestAsyncPackage.cs usuń metodę i zastąp Initialize() metodę InitializeAsync() . Dodaj usługę i dodaj metodę wywołania zwrotnego, aby utworzyć usługi. Oto przykład asynchronicznego inicjatora dodającego usługę:

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

    Aby ustawić tę usługę jako widoczną poza tym pakietem, ustaw wartość flagi podwyższania poziomu na true jako ostatni parametr: this.AddService(typeof(STextWriterService), CreateTextWriterService, true);

  2. Dodaj odwołanie do biblioteki Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll.

  3. Zaimplementuj metodę wywołania zwrotnego jako metodę asynchroniczną, która tworzy i zwraca usługę.

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

Korzystanie z usługi

Teraz możesz pobrać usługę i użyć jej metod.

  1. Pokażemy to w inicjatorze, ale możesz uzyskać usługę w dowolnym miejscu, w którym chcesz korzystać z usługi.

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

    Nie zapomnij zmienić userpath nazwy pliku i ścieżki, która ma sens na maszynie!

  2. Skompiluj i uruchom kod. Po wyświetleniu eksperymentalnego wystąpienia programu Visual Studio otwórz rozwiązanie. AsyncPackage Powoduje to automatyczne ładowanie. Po uruchomieniu inicjatora należy znaleźć plik w określonej lokalizacji.

Używanie usługi asynchronicznej w procedurze obsługi poleceń

Oto przykład użycia usługi asynchronicznej w poleceniu menu. Możesz użyć procedury pokazanej tutaj, aby użyć usługi w innych metodach innych niż asynchroniczne.

  1. Dodaj polecenie menu do projektu. (W Eksplorator rozwiązań wybierz węzeł projektu, kliknij prawym przyciskiem myszy i wybierz polecenie Dodaj>nowy element>Rozszerzalność>niestandardowego polecenia. Nadaj plikowi poleceń nazwę TestAsyncCommand.cs.

  2. Niestandardowy szablon polecenia ponownie dodaje metodę Initialize() do pliku TestAsyncPackage.cs w celu zainicjowania polecenia. W metodzie Initialize() skopiuj wiersz, który inicjuje polecenie. Powinien on wyglądać następująco:

    TestAsyncCommand.Initialize(this);
    

    Przenieś ten wiersz do InitializeAsync() metody w pliku AsyncPackageForService.cs . Ponieważ jest to w inicjalizacji asynchronicznej, należy przełączyć się do głównego wątku przed zainicjowaniem polecenia przy użyciu polecenia SwitchToMainThreadAsync. Informacje powinny teraz wyglądać następująco:

    
    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. Usuń metodę Initialize() .

  4. W pliku TestAsyncCommand.cs znajdź metodę MenuItemCallback() . Usuń treść metody .

  5. Dodaj dyrektywę using:

    using System.IO;
    
  6. Dodaj metodę asynchroniczną o nazwie UseTextWriterAsync(), która pobiera usługę i używa jej metod:

    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. Wywołaj tę metodę z MenuItemCallback() metody :

    private void MenuItemCallback(object sender, EventArgs e)
    {
        UseTextWriterAsync();
    }
    
    
  8. Skompiluj rozwiązanie i rozpocznij debugowanie. Po wyświetleniu eksperymentalnego wystąpienia programu Visual Studio przejdź do menu Narzędzia i wyszukaj element menu Invoke TestAsyncCommand . Po kliknięciu kontrolki TextWriterService zapisuje określony plik. (Nie musisz otwierać rozwiązania, ponieważ wywołanie polecenia powoduje również załadowanie pakietu).