Visual Studio 解决方案依赖关系可视化工具

在处理包含大量项目的Visual Studio解决方案时,经常需要理解项目之间的依赖关系。虽然市面上存在一些工具和应用程序可以展示这些依赖关系,但它们要么只支持旧版本的Visual Studio,要么生成的图表杂乱无章,难以阅读。因此,决定创建一个基于文本的、树状视图的依赖关系浏览器,它简单直观,能够展示项目的依赖关系,无论是以层次结构还是扁平列表的形式。

功能概述

这个工具提供了以下功能:

  • 层次结构视图:允许用户深入查看每个项目的依赖关系。
  • 扁平列表视图:自动展开项目依赖关系,以列表形式展示所有唯一的依赖项。
  • 依赖关系树状视图:展示其他项目依赖的项目。

局限性

尽管这个工具很有用,但它也有一些局限性:

  • 可能在Visual Studio2010及以后的版本中过时。
  • 仅支持C#项目,不解析其他类型的项目。
  • 仅支持Visual Studio2008解决方案文件。

层次结构视图

层次结构视图允许用户逐层查看每个项目的依赖关系。这种视图对于理解项目之间的复杂关系非常有用。

扁平列表视图

在扁平列表视图中,应用程序会自动展开项目的依赖关系,并以列表形式展示所有唯一的依赖项。这种视图有助于快速识别项目依赖的全局视图。

项目的依赖关系

用户可以选择一个项目,并查看哪些项目引用了所选项目。这有助于理解项目在解决方案中的作用和重要性。

代码实现

这个工具的代码非常简单,几乎没有错误检查,基本上是直接实现。值得注意的是,解决方案文件不是XML文档,而项目文件(csproj)是。这让人不禁好奇。

读取解决方案文件涉及大量的字符串检查和处理,创建一个包含项目名称和项目路径的字典。

public class Solution { protected Dictionary<string, string> projectPaths; public Dictionary<string, string> ProjectPaths { get { return projectPaths; } } public Solution() { projectPaths = new Dictionary<string, string>(); } public void Read(string filename) { StreamReader sr = new StreamReader(filename); string solPath = Path.GetDirectoryName(filename); while (!sr.EndOfStream) { string line = sr.ReadLine(); if (!String.IsNullOrEmpty(line)) { if (line.StartsWith("Project")) { string projName = StringHelpers.Between(line, '=', ','); projName = projName.Replace("\"", "").Trim(); string projPath = StringHelpers.RightOf(line, ','); projPath = StringHelpers.Between(projPath, "\"", "\""); projPath = projPath.Replace("\"", "").Trim(); if (projPath.EndsWith(".csproj")) { projPath = Path.Combine(solPath, projPath); projectPaths.Add(projName, projPath); } } } } sr.Close(); } }

解决方案文件不同,项目文件是一个XML文件。在处理时,需要指定XML命名空间。

public class Project { protected Dictionary<string, string> referencedProjects; protected List<Project> dependencies; public string Name { get; set; } public Dictionary<string, string> ReferencedProjects { get { return referencedProjects; } } public List<Project> Dependencies { get { return dependencies; } } public Project() { referencedProjects = new Dictionary<string, string>(); dependencies = new List<Project>(); } public void Read(string filename) { XDocument xdoc = XDocument.Load(filename); XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003"; foreach (var projRef in from el in xdoc.Root.Elements(ns + "ItemGroup").Elements(ns + "ProjectReference") select new { Path = el.Attribute("Include").Value, Name = el.Element(ns + "Name").Value }) { string projPath = Path.GetDirectoryName(filename); projPath = Path.Combine(projPath, projRef.Path); referencedProjects.Add(projRef.Name, projPath); } } }

使用一个单独的方法将解决方案和项目结合起来,创建另一个字典,这次键是项目名称,值是Project实例。这个方法还填充了Project类的Dependencies集合。

protected Dictionary<string, Project> projects; protected void ParseSolution(string filename) { projects = new Dictionary<string, Project>(); Solution sol = new Solution(); sol.Read(filename); foreach (KeyValuePair<string, string> kvp in sol.ProjectPaths) { Project proj = new Project(); proj.Name = kvp.Key; proj.Read(kvp.Value); projects.Add(proj.Name, proj); } foreach (KeyValuePair<string, Project> kvp in projects) { foreach (string refProjName in kvp.Value.ReferencedProjects.Keys) { Project refProject = projects[refProjName]; kvp.Value.Dependencies.Add(refProject); } } }

填充依赖关系图有两种方式:层次结构或扁平化。两种方式的代码相似,主要区别在于是否创建子节点。

protected void PopulateNewLevel(TreeNode node, ICollection<Project> projects) { List<string> nodeNames = new List<string>(); foreach (Project p in projects) { TreeNode tn = new TreeNode(p.Name); node.Nodes.Add(tn); if (asTree) { PopulateNewLevel(tn, p.Dependencies); } else { PopulateSameLevel(tn, p.Dependencies); } } } protected void PopulateSameLevel(TreeNode node, ICollection<Project> projects) { foreach (Project p in projects) { bool found = false; foreach (TreeNode child in node.Nodes) { if (child.Text == p.Name) { found = true; break; } } if (!found) { TreeNode tn = new TreeNode(p.Name); node.Nodes.Add(tn); } PopulateSameLevel(node, p.Dependencies); } } protected void PopulateDependencyOfProjects(TreeNode node, ICollection<Project> projects) { foreach (Project p in projects) { TreeNode tn = new TreeNode(p.Name); node.Nodes.Add(tn); foreach (Project pdep in projects) { foreach (Project dep in pdep.Dependencies) { if (p.Name == dep.Name) { bool found = false; foreach (TreeNode tnDep in tn.Nodes) { if (tnDep.Text == pdep.Name) { found = true; break; } } if (!found) { TreeNode tn2 = new TreeNode(pdep.Name); tn.Nodes.Add(tn2); } } } } } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485