Special thanks to @JasonMalinowski for his help clarifying some of the subtleties of the workspace API. Until this point, we’ve simply been constructing syntax trees from strings. This approach works well when creating short samples, but often we’d like to work with entire solutions. Enter: Workspaces. Workspaces are the root node of a C# hierarchy that consists of a solution, child projects and child documents. A fundamental tenet within Roslyn is that most objects are immutable. This means we can’t hold on to a reference to a solution and expect it to be up-to-date forever. The moment a change is made, this solution will be out of date and a new, updated solution will have been created. Workspaces are our root node. Unlike solutions, projects and documents, they won’t become invalid and always contain a reference to the current, most up-to-date solution. There are four Workspace variants to consider:
Workspace
The abstract base class for all other workspaces. It’s a little disingenuous to claim that it’s a workspace variant, as you’ll never actually have an instance of it. Instead, this class serves as a sort of API around which actual workspace implementations can be created. It can be tempting to think of workspaces solely within the context of Visual Studio. After all, for most C# developers this is the only way we’ve dealt with solutions and projects. However, Workspace is meant to be agnostic as to the physical source of the files it represents. Individual implementations might store the files on the local filesystem, within a database, or even on a remote machine. One simply inherits from this class and overrides Workspace’s empty implementations as they see fit.
MSBuildWorkspace
A workspace that has been built to handle MSBuild solution (.sln) and project (.csproj, .vbproj) files. Unfortunately it cannot currently write to .sln files, which means we can’t use it to add projects or create new solutions.
The following example shows how we can iterate over all the documents in a solution:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
string solutionPath = @"C:\Users\…\PathToSolution\MySolution.sln"; | |
var msWorkspace = MSBuildWorkspace.Create(); | |
var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result; | |
foreach (var project in solution.Projects) | |
{ | |
foreach (var document in project.Documents) | |
{ | |
Console.WriteLine(project.Name + "\t\t\t" + document.Name); | |
} | |
} |
For more information see Learn Roslyn Now – E06 – MSBuildWorkspace.
AdhocWorkspace
A workspace that allows one to add solution and project files manually. One should note that the API for adding and removing solution items is different within AdhocWorkspace when compared to the other workspaces. Instead of calling TryApplyChanges(), methods for adding projects and documents are provided at the workspace level. This workspace is meant to be consumed by those who just need a quick and easy way to create a workspace and add projects and documents to it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var workspace = new AdhocWorkspace(); | |
string projName = "NewProject"; | |
var projectId = ProjectId.CreateNewId(); | |
var versionStamp = VersionStamp.Create(); | |
var projectInfo = ProjectInfo.Create(projectId, versionStamp, projName, projName, LanguageNames.CSharp); | |
var newProject = workspace.AddProject(projectInfo); | |
var sourceText = SourceText.From("class A {}"); | |
var newDocument = workspace.AddDocument(newProject.Id, "NewFile.cs", sourceText); | |
foreach (var project in workspace.CurrentSolution.Projects) | |
{ | |
foreach (var document in project.Documents) | |
{ | |
Console.WriteLine(project.Name + "\t\t\t" + document.Name); | |
} | |
} |
For more information see Learn Roslyn Now – E08 – AdhocWorkspace
VisualStudioWorkspace
The active workspace consumed within Visual Studio packages. As this workspace is tightly integrated with Visual Studio, it’s difficult to provide a small example on how to use this workspace. Steps:
- Create a new VSPackage.
- Add a reference to the
Microsoft.VisualStudio.LanguageServices.dll
. It’s now available on NuGet. - Navigate to the <VSPackageName>Package.cs file (where <VSPackageName> is the name you chose for your solution.
- Find the Initalize() method.
- Place the following code within Initialize()
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protected override void Initialize() | |
{ | |
//Other stuff… | |
... | |
var componentModel = (IComponentModel)this.GetService(typeof(SComponentModel)); | |
var workspace = componentModel.GetService<Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace>(); | |
} | |
//Alternatively you can MEF import the workspace. MEF can be tricky if you're not familiar with it | |
//but here's how you'd import VisuaStudioWorkspace as a property. | |
[Import(typeof(Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace))] | |
public VisualStudioWorkspace myWorkspace { get; set; } |
When writing VSPackages, one of the most useful pieces of functionality exposed by the workspace is the WorkspaceChanged event. This event allows our VSPackage to respond to any changes made by the user or any other VSPackage. Naturally, the best way to familiarize oneself with workspaces is to use them. Roslyn’s immutability can impose a slight learning curve so we’ll be exploring how to modify documents and projects in future posts.
For more information see Learn Roslyn Now – E07 – Visual StudioWorkspace
You can use Roslyn on MSBuild 4.0 / VS2012 too now 😉 http://stackoverflow.com/questions/26323145/use-roslyn-msbuildworkspace-with-visual-studio-2013/27262785#27262785
It looks like CustomWorkspace no longer exists, and has been replaced by AdhocWorkspace.
Thanks for the heads up! I updated the text, but forgot to update the code sample. I’ll make that change now.
You can now download the “Build 14” tools separately.
In the following page, click “Additional Tools”, then “Microsoft Build Tools 2015 RC”.
https://www.visualstudio.com/en-us/downloads/visual-studio-2015-downloads-vs.aspx
I just want to load up the current solution into a file I can traverse. Is this possible?
I’m not sure what you mean. You can load solutions (.sln files) from disk via MSBuildWorkspace. Then you can traverse the projects and documents in that workspace if you’d like to. Or were you trying to do something else?
You pretty much nailed it. I actually made some progress. I was having trouble getting an instance of any kind of workspace. Turns out I was missing some dependencies and the code tips weren’t telling me. I lean a bit too much on visual studio for help these days I guess. It’s been a pain trying to learn Roslyn on release day when there are 4 years of outdated and incomplete docs. Luckily I found this site, and it put me on the right path. Thanks for the quick response either way. 2:30am responses are legit.
I’m in need of a little help. I have loaded a project and I’m checking the Documents property, but only the CS files are there. I can’t find any XML content files. I’m using MsBuildWorkspace to load the project. Can someone tell me where I can find the non-code files?
I have some code analyzers that are configurable with a menu dialog. After the user sets some options I want all my analyzers to run again since the results will be different. I know how to read the configuration information in my dialog box from the Analyzer but I don’t know how to tell Visual Studio that it needs to rerun the analyzers. Can I raise the WorkspaceChanged event, (if so how) and will it do what I want or do I need to do something else? My code is in VB if that matters.
I was able to load a MSBuild Workspace without having to install either the 2013 and 2014 MSBuild tools. Was dreading the 4 GB install.