工作区
工作区是 Visual Studio 表示打开文件夹中的任何文件集合的方式,它由 IWorkspace 类型表示。 工作区本身不了解与文件夹内的文件相关的内容或功能。 相反,它为功能和扩展提供了一组通用的 API,用于生成和使用其他人可以操作的数据。 生成器是通过使用各种导出属性的 Managed Extensibility Framework (MEF) 组成的。
工作区提供程序和服务提供对工作区内容做出反应的数据和功能。 它们可能提供上下文文件信息、源文件中的符号或生成功能。
这两个概念都使用工厂模式,并由工作区通过 MEF 导入。 所有导出属性都会实施 IProviderMetadataBase
或 IWorkspaceServiceFactoryMetadata
,但对于一些具体类型,应对导出的类型使用扩展。
提供程序和服务之间的一个区别在于它们与工作区的关系。 工作区可以有多个特定类型的提供程序,但每个工作区只能创建一个特定类型的服务。 例如,工作区包含许多文件扫描程序提供程序,但每个工作区只有一个索引服务。
另一个关键区别是对来自提供程序和服务的数据的使用。 工作区是出于多种原因从提供程序获取数据的入口点。 首先,提供程序通常具有一些它们创建的窄数据集。 数据可能是 C# 源文件的符号,也可能是 CMakeLists.txt
文件的生成文件上下文。 工作区会将使用者的请求与元数据与请求一致的提供程序相匹配。 其次,某些应用场景允许许多提供者参与请求,而其他应用场景则使用具有最高优先级的提供程序。
相比之下,扩展可以获取工作区服务的实例并直接与工作区服务交互。 可用的 IWorkspace
扩展方法适用于 Visual Studio 提供的服务,例如 GetFileWatcherService。 扩展可以为扩展中的组件或其他扩展提供工作区服务。 使用者应使用 GetServiceAsync 或在 IWorkspace
类型上提供的扩展方法。
警告
不要创作与 Visual Studio 冲突的服务。 这可能会导致意外问题。
关闭工作区时,扩展程序可能需要处理但却调用了异步代码。 IAsyncDisposable 接口可用于轻松编写此代码。
- IWorkspace 是打开的工作区(如打开的文件夹)的中心实体。
- IWorkspaceProviderFactory<T> 会为每个实例化工作区创建一个提供程序。
- IWorkspaceServiceFactory 会为每个实例化工作区创建一个服务。
- IAsyncDisposable 应在处理过程中,在需要运行异步代码的提供程序和服务上实施。
- WorkspaceServiceHelper 提供用于访问已知服务或任意服务的帮助程序方法。
工作区具有 IWorkspaceSettingsManager 服务,可以对工作区进行简单但强大的控制。 有关设置的基本概述,请参阅自定义生成和调试任务。
大多数 SettingsType
类型的设置是 .json
文件,例如 VSWorkspaceSettings.json
和 tasks.vs.json。
工作区设置的强大功能围绕“作用域”,即工作区中的路径。 当使用者调用 GetAggregatedSettings 时,将聚合包含所请求路径和设置类型的所有作用域。 作用域聚合优先级如下所示:
- “本地设置”,通常是工作区根目录
.vs
。 - 所请求路径本身。
- 所请求路径的父目录。
- 所有更高层级的父目录,直至并包括工作区根目录。
- “全局设置”,位于用户目录中。
最后将得到 IWorkspaceSettings 的实例。 此对象保存特定类型的设置,并且可以查询用于设置存储为 string
的密钥名称。 GetProperty 方法和 WorkspaceSettingsExtensions 扩展方法要求调用方知道所请求的设置值的类型。 由于大多数设置文件都保留为 .json
文件,因此许多调用将使用这些类型的 string
、bool
int
和数组。 此外还支持对象类型。 在这些情况下,可以使用 IWorkspaceSettings
本身作为类型参数。 例如:
{
"intValue": 1,
"stringValue": "s",
"boolValue": true,
"stringArray": [
"s1",
"s2"
],
"nestedIWorkspaceSettings": {
"nestedString": "ns"
}
}
假设这些设置位于用户的 VSWorkspaceSettings.json 中,则可以按如下方式访问数据:
using System.Collections.Generic;
using Microsoft.VisualStudio.Workspace;
using Microsoft.VisualStudio.Workspace.Settings;
private static void ReadSettings(IWorkspace workspace)
{
IWorkspaceSettingsManager settingsManager = workspace.GetSettingsManager();
IWorkspaceSettings settings = settingsManager.GetAggregatedSettings(SettingsTypes.Generic);
// result == WorkspaceSettingsResult.Success
WorkspaceSettingsResult result = settings.GetProperty("intValue", out int intValue);
result = settings.GetProperty("stringValue", out string stringValue);
result = settings.GetProperty("boolValue", out bool boolValue);
result = settings.GetProperty("stringArray", out string[] stringArray);
result = settings.GetProperty("nestedIWorkspaceSettings", out IWorkspaceSettings nestedIWorkspaceSettings);
result = nestedIWorkspaceSettings.GetProperty("nestedString", out string nestedString);
// Extension method alternative using default values.
int intValueOrDefault = settings.Property("intValue", /* default */ 42);
// Missing key. result == WorkspaceSettingsResult.Undefined
result = settings.GetProperty("missing", out string missing);
// Wrong type for a key. result == WorkspaceSettingsResult.Error
result = settings.GetProperty("intValue", out IWorkspaceSettings notSettings);
// Special ability to union "stringArray" across all scopes.
IEnumerable<string> allStringArray = settings.UnionPropertyArray<string>("stringArray");
}
备注
这些设置 API 与 Microsoft.VisualStudio.Settings
命名空间中可用的 API 无关。 工作区设置与主机无关,并使用特定于工作区的设置文件或动态设置提供程序。
扩展可以提供 IWorkspaceSettingsProvider。 这些内存中提供程序允许扩展添加设置或替代其他项。
导出 IWorkspaceSettingsProvider
与其他工作区提供程序不同。 工厂不是 IWorkspaceProviderFactory
,没有特殊的属性类型。 相反,它实施 IWorkspaceSettingsProviderFactory 并使用 [Export(typeof(IWorkspaceSettingsProviderFactory))]
。
// Common workspace provider factory pattern
[ExportFeatureProvider(some, args, to, export)]
internal class MyProviderFactory : IWorkspaceProviderFactory<IFeatureProvider>
{
IFeatureProvider CreateProvider(IWorkspace workspace) => new Provider(workspace);
}
// IWorkspaceSettingsProvider pattern
[Export(typeof(IWorkspaceSettingsProviderFactory))]
internal class MySettingsProviderFactory : IWorkspaceSettingsProviderFactory
{
// 100 is typically the value used by built-in settings providers. Lower value is higher priority.
int Priority => 100;
IWorkspaceSettingsProvider CreateSettingsProvider(IWorkspace workspace) => new MySettingsProvider(workspace);
}
提示
实施返回 IWorkspaceSettingsSource
的方法(如 IWorkspaceSettingsProvider.GetSingleSettings
)时,将返回 IWorkspaceSettings
的一个实例而不是 IWorkspaceSettingsSource
。 IWorkspaceSettings
提供在某些设置聚合期间有用的详细信息。
- IWorkspaceSettingsManager 可读取和聚合工作区的设置。
- GetSettingsManager 可获取工作区的
IWorkspaceSettingsManager
。 - GetAggregatedSettings 可获取跨所有重叠范围聚合的给定作用域的设置。
- IWorkspaceSettings 包含针对特定作用域的设置。
- 从
IWorkspaceProviderFactory.CreateProvider
或在创建时记住其Workspace
上下文的类似 API 返回对象。 编写提供程序接口时,预期此对象在创建时会保留。 - 将特定于工作区的缓存或设置保存在工作区的“本地设置”路径内。 在 Visual Studio 2017 版本 15.6 或更高版本中使用
Microsoft.VisualStudio.Workspace.WorkspaceHelper.MakeRootedUnderWorkingFolder
为文件创建路径。 对于版本 15.6 之前的版本,请使用以下代码片段:
using System.IO;
using Microsoft.VisualStudio.Workspace;
using Microsoft.VisualStudio.Workspace.Settings;
private static string MakeRootedUnderWorkingFolder(IWorkspace workspace, string relativePath)
{
string workingFolder = workspace.GetSettingsManager().GetAggregatedSettings(SettingsTypes.WorkspaceControlSettings).Property<string>("WorkingFolder");
return Path.Combine(workingFolder, relativePath);
}
加载的包可以实施 IVsSolutionEvents7
并调用 IVsSolution.AdviseSolutionEvents
。 其中包括在 Visual Studio 中打开和关闭文件夹时的事件处理。
UI 上下文可用于自动加载包。 该值为 4646B819-1AE0-4E79-97F4-8A8176FDD664
。
工作区扩展性严重基于 MEF,组合错误将导致承载“打开文件夹”的包无法加载。 例如,如果扩展导出了一个包含 ExportFileContextProviderAttribute
的类型,但该类型仅实施 IWorkspaceProviderFactory<IFileContextActionProvider>
,则尝试在 Visual Studio 中打开文件夹时将发生错误。
有关错误详细信息,请参阅 %LOCALAPPDATA%\Microsoft\VisualStudio\16.0_id\ComponentModelCache\Microsoft.VisualStudio.Default.err。 解决扩展实施的类型的任何错误。