We want to migrate our big application with around 150 projects to .Net 6. To do that, we need to find a sequence to migrate the different projects while we keep their dependencies in place. Otherwise we will have an endless list of problems that prevents us from compiling our code.
Let’s combine the power of Roslyn to analyse our Visual Studio solutions with the network algorithms of the Python library NetworkX.
Preparations
Make sure that you create a .Net 7 project to read the .Net 4.8 solution as it was described in the last post.
I will use once more the NunitUpgrade solution as a stand in. It has enough details to understand the problem, but it is small enough to post the whole generated code.
Read the solution with Roslyn
We need to register the MS Build tools and load the Visual Studio solution file into a workspace:
1 2 3 4 5 6 7 |
string solutionPath = @"..\..\..\..\..\..\NUnit\NunitUpgrade\NunitUpgrade.sln"; // Register MSBuild MSBuildLocator.RegisterDefaults(); var workspace = MSBuildWorkspace.Create(); var solution = await workspace.OpenSolutionAsync(solutionPath); |
Generate Python code
We start with a StringBuilder to collect all the Python statements we generate. The Python code looks boring and unspectacular, but it will work as long as we are careful how we indent it. In Python the whitespaces matter, so we must ensure that they are correct – otherwise the code will not work.
Our first Python lines are the import of NetworkX and the creation of a directed graph. For the project dependencies the direction of the dependency is important.
We iterate through all projects and add their names as nodes. That way we forget no projects, even if no other project depends on them. Then we iterate again through all projects and create an edge for each referenced project.
We end our Python code with two method calls to check if the nodes and edges make sense. As the last step we write the whole program into the file project_graph.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var python = new StringBuilder(); python.AppendLine("import networkx as nx\n"); python.AppendLine("G = nx.DiGraph()"); var names = new Dictionary<Guid, string>(); foreach (var project in solution.Projects) { names[project.Id.Id] = project.Name; python.AppendLine($"G.add_node('{project.Name}')"); } foreach (var project in solution.Projects) { foreach (var reference in project.AllProjectReferences) { python.AppendLine( $"G.add_edge('{project.Name}', '{names[reference.ProjectId.Id]}')"); } } python.AppendLine("\nprint(f'Nodes: {G.number_of_nodes()}')"); python.AppendLine("print(f'Edged: {G.number_of_edges()}')"); File.WriteAllText("project_graph.py", python.ToString()); |
The result
If we run our Roslyn application, it will read the project dependencies and create us a project_graph.py file with this Python code:
1 2 3 4 5 6 7 8 9 10 11 |
import networkx as nx G = nx.DiGraph() G.add_node('MyBizLogic') G.add_node('TestsForNUnit2') G.add_node('TestsForNUnit3') G.add_edge('TestsForNUnit2', 'MyBizLogic') G.add_edge('TestsForNUnit3', 'MyBizLogic') print(f'Nodes: {G.number_of_nodes()}') print(f'Edged: {G.number_of_edges()}') |
We can run this file in the Python interpreter to check if the nodes and edges match our expectations:
1 |
python project_graph.py |
Nodes: 3
Edged: 2
Next
We turned the project dependencies from our Visual Studio solution into a graph for NetworkX. In the next Python Friday edition, we take it from here and turn our generated code into the migration sequence we are interested in.
1 thought on “Turn .Net Project Dependencies Into Python Code With Roslyn”