Roslyn to nie tylko parsowanie kodu, ale również zarządzanie projektem\solucją za pomocą WorkspaceAPI.
Pisząc różne narzędzia dla programistów, oprócz analizy kodu, zwykle chcemy mieć informacje o kontekście danego kodu – np. nazwie pliku czy projekcie w którym znajduje się dana klasa.
Workspace API, jak nie trudno domyślić się, opiera się na abstrakcyjnej klasie Workspace. Zwykle jednak pracować będziemy z MSBuildWorkspace, która pozwala nam zarządzać .sln czy .csproj:
MSBuildWorkspace msWorkspace = MSBuildWorkspace.Create(); Solution solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;
Wywołując OpenSolutionAsync na Workspace, otwieramy po prostu solucję. Zmienna solutionPath to zwykły string wskazujący do pliku .sln. Po otwarciu, możemy wylistować wszystkie projekty i pliki:
foreach (Project project in solution.Projects) { Console.WriteLine(project.Name); foreach (Document document in project.Documents) { Console.WriteLine("\t{0}",document.FilePath); } }
API pozwala również na zarządzanie referencjami:
foreach (Project project in solution.Projects) { foreach (ProjectReference projectReference in project.ProjectReferences) { Console.WriteLine(projectReference.ProjectId); } foreach (var metaReference in project.MetadataReferences) { Console.WriteLine(metaReference.ToString()); } }
ProjectReferencje to referencja do innego projektu w tej samej solucji. Z kolei za pomocą MetaReference uzyskujemy dostęp do wszystkich innych bibliotek z których korzystamy, włączając przy tym systemowe biblioteki.
Do dyspozycji jest również AdhocWorkspace, klasa służąca do tworzenia całych solucji i projektów od zera:
var workspace = new AdhocWorkspace(); const string projectName = "ProjectA"; ProjectId projectId = ProjectId.CreateNewId(); ProjectInfo projectInfo = ProjectInfo.Create(projectId, VersionStamp.Create(), projectName, projectName, LanguageNames.CSharp); Project projectA = workspace.AddProject(projectInfo); var classCode = SourceText.From("class SampleClass {}"); var newDocument = workspace.AddDocument(projectA.Id, "SampleClass.cs", classCode);
Jeśli piszemy rozszerzenia do Visual Studio, wtedy zdecydowanie powinniśmy zainteresować się VisualStudioWorkspace. Wcześniej zarządzanie projektem było dość kłopotliwe, a teraz wystarczy, że z rozszerzenia uzyskamy dostęp do VisualStudioWorkspace za pomocą importu MEF:
[Import(typeof(Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace))] public VisualStudioWorkspace myWorkspace { get; set; }
Jeśli piszemy plugin, liczący metryki kodu, wtedy za pomocą VisualStudioWorkspace wiemy, kiedy użytkownik dodaje nowy plik, projekt czy modyfikuje jakąś konfigurację. Klasa eksponuje zdarzenia takie jak WorkspaceChanged, które wystarczy po prostu monitorować.