Proporcionar un servicio asincrónico de Visual Studio
Si desea obtener un servicio sin bloquear el subproceso de interfaz de usuario, debe crear un servicio asincrónico y cargar el paquete en un subproceso en segundo plano. Para este propósito, puede usar en AsyncPackage lugar de y Packageagregar el servicio con los métodos asincrónicos especiales del paquete asincrónico.
Para obtener información sobre cómo proporcionar servicios de Visual Studio sincrónicos, vea Cómo: Proporcionar un servicio.
Implementación de un servicio asincrónico
Cree un proyecto VSIX (Archivo>nuevo>proyecto>visual C#>Extensiblity>VSIX Project). Asigne al proyecto el nombre TestAsync.
Agregue un VSPackage al proyecto. Seleccione el nodo del proyecto en el Explorador de soluciones y haga clic en Agregar>nuevo elemento>Visual C# Elementos>extensibilidad>paquete de Visual Studio. Asigne a este archivo el nombre TestAsyncPackage.cs.
En TestAsyncPackage.cs, cambie el paquete para heredar de en lugar de
AsyncPackage
Package
:public sealed class TestAsyncPackage : AsyncPackage
Para implementar un servicio, debe crear tres tipos:
Interfaz que identifica el servicio. Muchas de estas interfaces están vacías, es decir, no tienen métodos, ya que solo se usan para consultar el servicio.
Interfaz que describe la interfaz de servicio. Esta interfaz incluye los métodos que se van a implementar.
Clase que implementa tanto el servicio como la interfaz de servicio.
En el ejemplo siguiente se muestra una implementación muy básica de los tres tipos. El constructor de la clase de servicio debe establecer el proveedor de servicios. En este ejemplo, solo agregaremos el servicio al archivo de código del paquete.
Agregue las siguientes directivas using al archivo de paquete:
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;
Esta es la implementación del servicio asincrónico. Tenga en cuenta que debe establecer el proveedor de servicios asincrónico en lugar del proveedor de servicios sincrónico en el constructor:
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); }
Registro de un servicio
Para registrar un servicio, agregue al ProvideServiceAttribute paquete que proporciona el servicio. Diferente de registrar un servicio sincrónico, debe asegurarse de que tanto el paquete como el servicio admiten la carga asincrónica:
Debe agregar el campo AllowsBackgroundLoading = true a PackageRegistrationAttribute para asegurarse de que el paquete se puede inicializar de forma asincrónica Para obtener más información sobre PackageRegistrationAttribute, vea Registrar y anular el registro de VSPackages.
Debe agregar el campo IsAsyncQueryable = true a ProvideServiceAttribute para asegurarse de que la instancia de servicio se puede inicializar de forma asincrónica.
Este es un ejemplo de con
AsyncPackage
un registro de servicio asincrónico:
[ProvideService((typeof(STextWriterService)), IsAsyncQueryable = true)]
[ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(TestAsyncPackage.PackageGuidString)]
public sealed class TestAsyncPackage : AsyncPackage
{. . . }
Agregar un servicio
En TestAsyncPackage.cs, quite el
Initialize()
método e invalide elInitializeAsync()
método . Agregue el servicio y agregue un método de devolución de llamada para crear los servicios. Este es un ejemplo del inicializador asincrónico que agrega un servicio:protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress) { await base.InitializeAsync(cancellationToken, progress); this.AddService(typeof(STextWriterService), CreateTextWriterService); }
Para que este servicio sea visible fuera de este paquete, establezca el valor de marca de promoción en true como último parámetro:
this.AddService(typeof(STextWriterService), CreateTextWriterService, true);
Agregue una referencia a Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll.
Implemente el método de devolución de llamada como un método asincrónico que crea y devuelve el servicio.
public async Task<object> CreateTextWriterService(IAsyncServiceContainer container, CancellationToken cancellationToken, Type serviceType) { TextWriterService service = new TextWriterService(this); await service.InitializeAsync(cancellationToken); return service; }
Uso de un servicio
Ahora puede obtener el servicio y usar sus métodos.
Lo mostraremos en el inicializador, pero puede obtener el servicio en cualquier lugar en el que quiera usar el servicio.
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"); }
No olvide cambiar
userpath
a un nombre de archivo y ruta de acceso que tenga sentido en su máquina.Compile y ejecute el código. Cuando aparezca la instancia experimental de Visual Studio, abra una solución. Esto hace que se
AsyncPackage
descargue automáticamente. Cuando se haya ejecutado el inicializador, debe encontrar un archivo en la ubicación especificada.
Uso de un servicio asincrónico en un controlador de comandos
Este es un ejemplo de cómo usar un servicio asincrónico en un comando de menú. Puede usar el procedimiento que se muestra aquí para usar el servicio en otros métodos no asincrónicos.
Agregue un comando de menú al proyecto. (En Explorador de soluciones, seleccione el nodo del proyecto, haga clic con el botón derecho y seleccione Agregar>nuevo comando personalizado de extensibilidad>de elementos).> Asigne al archivo de comandos el nombre TestAsyncCommand.cs.
La plantilla de comandos personalizada vuelve a agregar el
Initialize()
método al archivo TestAsyncPackage.cs para inicializar el comando. En elInitialize()
método , copie la línea que inicializa el comando. Debería ser parecido a este:TestAsyncCommand.Initialize(this);
Mueva esta línea al
InitializeAsync()
método en el archivo AsyncPackageForService.cs . Dado que se trata de una inicialización asincrónica, debe cambiar al subproceso principal antes de inicializar el comando mediante SwitchToMainThreadAsync. Debería ser parecido a este: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); }
Elimine el
Initialize()
método .En el archivo TestAsyncCommand.cs , busque el
MenuItemCallback()
método . Elimine el cuerpo del método.Agregue una directiva using:
using System.IO;
Agregue un método asincrónico denominado
UseTextWriterAsync()
, que obtiene el servicio y usa sus métodos: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"); }
Llame a este método desde el
MenuItemCallback()
método :private void MenuItemCallback(object sender, EventArgs e) { UseTextWriterAsync(); }
Compile la solución y comience la depuración. Cuando aparezca la instancia experimental de Visual Studio, vaya al menú Herramientas y busque el elemento de menú Invocar TestAsyncCommand . Al hacer clic en él, TextWriterService escribe en el archivo especificado. (No es necesario abrir una solución, ya que invocar el comando también hace que el paquete se cargue).