Skip to content

How to use MSBuildWorkspace in Roslyn to analyse C# projects in .NET Interactive? #1985

@roberthusak

Description

@roberthusak

The package and version I'm asking about:
Version: 1.0.317502+9c2a225c543085a04516cc4fc490919d5925ba87
Build date: 2022-04-20T11:12:26.5754202Z
https://github.com/dotnet/interactive

Question

Hi, I'd like to use Microsoft Roslyn to analyse an existing C# project from a Jupyter notebook in VS Code, mainly to explore and visualise the code base. What is the best way to use Roslyn in .NET Interactive so that I don't stumble upon problems with wrongly loaded assemblies etc.?

I'm trying to load the project using the MSBuildWorkspace so that all the source files and references are loaded by MSBuild. To properly initialize MSBuild, MSBuildLocator is used:

#r "nuget:Microsoft.Build.Locator"
#r "nuget:Microsoft.CodeAnalysis.CSharp.Workspaces"
#r "nuget:Microsoft.CodeAnalysis.Workspaces.MSBuild"

using System.IO;
using System.Reflection;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis.MSBuild;

if (!MSBuildLocator.IsRegistered)
{
    MSBuildLocator.RegisterDefaults();
}

using (var workspace = MSBuildWorkspace.Create())
{
    var project = await workspace.OpenProjectAsync(@"C:\path\to\the.csproj");
}

However, I'm currently getting the following exception:

Error: System.TypeLoadException: Could not load type 'Microsoft.Build.Framework.Traits' from assembly 'Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
   at Microsoft.Build.Evaluation.ProjectCollection..ctor(IDictionary`2 globalProperties, IEnumerable`1 loggers, IEnumerable`1 remoteLoggers, ToolsetDefinitionLocations toolsetDefinitionLocations, Int32 maxNodeCount, Boolean onlyLogCriticalEvents, Boolean loadProjectsReadOnly)
   at Microsoft.Build.Evaluation.ProjectCollection..ctor(IDictionary`2 globalProperties)
   at Microsoft.CodeAnalysis.MSBuild.Build.ProjectBuildManager.StartBatchBuild(IDictionary`2 globalProperties) in /_/src/Workspaces/Core/MSBuild/MSBuild/Build/ProjectBuildManager.cs:line 161
   at Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.Worker.LoadAsync(CancellationToken cancellationToken) in /_/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs:line 134
   at Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.LoadProjectInfoAsync(String projectFilePath, ProjectMap projectMap, IProgress`1 progress, ILogger msbuildLogger, CancellationToken cancellationToken) in /_/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.cs:line 276
   at Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.OpenProjectAsync(String projectFilePath, ILogger msbuildLogger, IProgress`1 progress, CancellationToken cancellationToken) in /_/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs:line 251
   at Submission#3.<<Initialize>>d__0.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)

By attaching to the process and inspecting loaded assemblies via VS, I found that .NET Interactive is distributed with its own assembly Microsoft.Build.Framework.dll, which is loaded instead of the one located in the SDK folder (Microsoft.Build.dll is thanks to MSBuildLocator correctly loaded from the SDK folder):

roslyn_net_interactive_modules

The type Microsoft.Build.Framework.Traits is missing in the assembly distributed with .NET Interactive, hence the exception.

I wasn't able to force the runtime to load Microsoft.Build.Framework.dll from the SDK folder (AssemblyLoadContext.Default.LoadFromAssemblyPath etc.) instead of the one distributed with .NET Interactive. However, even if I succeeded, it doesn't even seem to me as the correct way to solve this, as it may cause other problems along the way. Do you have any suggestions how to approach this problem? And, maybe, how to approach assembly name collisions in .NET Interactive in general?

I might end up running MSBuild in the separate process and then creating the Roslyn Compilation object in the notebook manually (apart from the MSBuild stuff, Roslyn seems to work in notebooks), but I'd like to do it in a cleaner way, if there is any.

Thanks a lot in advance,
Robert

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions