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
Utwórz projekt VSIX (Plik>nowy>projekt>Visual C#>Extensiblity>VSIX Project). Nadaj projektowi nazwę TestAsync.
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.
W pliku TestAsyncPackage.cs zmień pakiet, aby dziedziczył z
AsyncPackage
, a niePackage
:public sealed class TestAsyncPackage : AsyncPackage
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.
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.
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;
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:
Aby upewnić się, że pakiet można zainicjować asynchronicznie, aby PackageRegistrationAttribute uzyskać więcej informacji na temat atrybutu PackageRegistrationAttribute, zobacz Rejestrowanie i wyrejestrowywanie pakietów VSPackages.
Musisz dodać pole IsAsyncQueryable = true , ProvideServiceAttribute aby upewnić się, że wystąpienie usługi można zainicjować asynchronicznie.
Oto przykład
AsyncPackage
rejestracji usługi asynchronicznej:
[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
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);
Dodaj odwołanie do biblioteki Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll.
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.
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!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.
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.
Niestandardowy szablon polecenia ponownie dodaje metodę
Initialize()
do pliku TestAsyncPackage.cs w celu zainicjowania polecenia. W metodzieInitialize()
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); }
Usuń metodę
Initialize()
.W pliku TestAsyncCommand.cs znajdź metodę
MenuItemCallback()
. Usuń treść metody .Dodaj dyrektywę using:
using System.IO;
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"); }
Wywołaj tę metodę z
MenuItemCallback()
metody :private void MenuItemCallback(object sender, EventArgs e) { UseTextWriterAsync(); }
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).