1

我目前使用一个包含大约 100 个项目的大型解决方案。至少有 10 个项目是可执行应用程序。一些库项目是通过 MEF 和反射而不是直接引用作为插件导入的。如果需要的插件自己的依赖没有复制到使用它的可执行项目的输出或插件目录,我们将在运行时得到反射错误。

我们已经尝试或讨论了以下解决方案,但它们似乎都不合适:

  1. “硬”引用:最初,我们让可执行项目引用他们需要的其他项目,即使它们最终将作为可选插件导入。这很快就失去了团队成员的青睐,他们需要构建排除某些插件并喜欢从一开始就卸载这些项目。这也使得很难使用 Resharper 或其他工具来清理未使用的引用并删除过时的第三方库,而不会意外地清除对所需插件自身依赖项的“未使用”引用。
  2. 构建后复制(使用预构建“拉”):在很短的一段时间内,一位高级团队成员将所有插件项目设置为将其输出输出自身 xcopy 到已知的“DependencyInjection”文件夹作为构建后事件。需要这些插件的项目将具有预构建事件,将每个所需的插件复制到它们自己的输出目录中。虽然这意味着插件项目“正确地”不知道它们可能在哪里使用,但这引起了两个主要问题。首先,任何时候对插件项目进行更改,他们都必须分别构建(按顺序)插件项目,然后是他们将测试它的可执行项目(以获取要复制的文件)。重建所有会更方便,但太慢了。第二,
  3. 构建后复制(推送):目前的解决方案从 xcopy 开始,现在主要在插件项目的构建后事件中使用 robocopy 将所需文件直接复制到使用它们的可执行项目的插件文件夹中。这非常有效,因为如果对插件进行更改,可以直接使用调试器运行。此外,CI 构建不会中断,并且为各种构建禁用某些“可选”插件项目的用户不会因缺少引用而出现构建错误。这似乎仍然很老套,并且在所有单独的构建后窗口中维护起来很麻烦,这些窗口很小且无法扩展。当可执行项目从项目重组中移出或重命名时,直到第二天听到隔夜自动化测试的结果后,我们才会发现损坏的引用。
  4. 带有引用的“虚拟”项目:一个简单的想法涉及为每个不同的可执行构建配置创建空项目,然后返回到硬引用方法。每个都将使用自己的引用来收集插件及其依赖项。他们还将引用实际的可执行文件并将其复制过来。然后,如果想在特定配置中运行特定的可执行文件,您将运行它的虚拟项目。这个似乎特别臃肿,从未尝试过。
  5. NuGet:在我对 NuGet 的有限熟悉中,这似乎非常适合使用包,但我不知道如何在一个解决方案内部实现该功能。我们已经讨论过拆分解决方案,但团队中的许多成员都强烈反对。是否可以将 NuGet 与来自同一解决方案的包一起使用?

这种情况的最佳做法是什么?是否有比上述任何一种更好的解决方案来管理像这样的反射依赖项的依赖关系,或者是上述最佳选择之一的改进?

4

3 回答 3

1

我曾经遇到过像你这样的情况。我们有近 100 个项目。我们也在使用MEFand System.AddIn。一开始我们有一些解决方案。我正在研究包含核心组件及其测试的核心解决方案。每个插件类别都在一个单独的解决方案中,包括合同、实现(一些插件有多个实现)和测试,以及一些测试主机以及核心程序集。稍后我们添加了一个包含所有项目的解决方案,在尝试了您提到的一些方法后,我们决定执行以下操作:

  1. 保留强制性的参考资料,
  2. 所有可执行项目都设置为输出到公共位置(一个用于调试,一个用于发布配置),
  3. 所有不应引用的项目都设置为输出到这些公共位置,
  4. 其他人引用的所有项目都保持不变,并且每个引用都设置为Copy Local = true.
  5. 测试保持不变。

尽管构建一切都很缓慢,但我们没有任何其他问题。当然,拥有近 100 个项目表明该设计可能过于模块化,正如Patrick建议的那样,我们应该尝试压缩它。

无论如何,您可以在几个小时内尝试这种方法,也许可以Copy Local = true尝试将 4 中提到的所有项目的输出文件夹设置为将其输出设置为公共位置,而不是设置。正如Patrick提到的,我们不知道此设置会减慢构建过程。

PS。我们从未尝试过使用NuGet,因为我们没有足够的资源和时间来尝试它。不过看起来很有希望。

于 2013-08-21T22:02:21.427 回答
1

好的,所以我在这个答案中假设每个开发人员需要不断地在本地拥有所有 100 个程序集(调试模式)才能完成其工作(开发、编译、冒烟测试、运行自动测试)。

您提到 RebuildAll 需要很长时间。通常这种症状是由太多的程序集 + 构建过程没有合理化造成的。所以首先要做的是尝试将 100 个程序集合并为尽可能少的程序集,并避免使用Copy Local = true. 效果将是更快(如 10 倍)的 RebuildAll 过程。请记住,程序集是物理制品,它们仅对物理事物有用(如插件、按需加载、测试/应用程序分离......)。我写了一本白皮书,详细介绍了我对该主题的看法:http ://www.ndepend.com/WhiteBooks.aspx

通过 .NET 程序集和 Visual Studio 项目对代码库进行分区(8 页)

  • 创建程序集的常见有效和无效原因
  • 提高 Visual Studio 解决方案的编译性能(快 10 倍)
  • 组织开发环境

在白皮书的建议中,一种想法是避免引用项目,而是引用程序集。这样,您有责任填写Project > right click > Project Dependencies将定义Project > right click > Project Build Order。如果您决定继续处理 100 个程序集,定义此设置需要付出努力,但作为奖励,高级(可执行)项目可以依赖于仅由反射使用的库,这将解决您的问题。

您是否根据 PDB 序列点数来衡量代码行?我估计,在 200K 到 300K 的限制之前,执行 RebuildAll(在白皮书中描述了优化)应该需要 5 到 10 秒(在一台像样的笔记本电脑上)并且它仍然可以接受。如果你的代码库非常大并且超出了这个限制,你需要打破我的第一个假设,找到一种开发人员不需要所有程序集来完成工作的方法(在这种情况下,我们可以进一步讨论) .

免责声明:此答案引用了我创建并现在管理其开发的工具 NDepend 站点的资源。

于 2013-08-21T15:16:07.430 回答
0

我们正在启动一个新项目,我正在寻找这个类似问题的“最佳实践”解决方案。对我们而言,我们可以将项目分为两类:1) 平台组件,提供全面的通用服务集;2) 执行特定业务功能的垂直组件。

过去,我们使用带有简单 UI 的 Visual Studio 插件,允许开发人员指定通用程序集路径来复制输出程序集,然后从通用程序集文件夹中引用所有程序集(无论它们位于不同的解决方案中的何处)。

我正在研究 NUGET,但创建和维护 NUGET 包所要做的纯粹工作是惩罚性的。

这是一个非常常见的场景,并且很想看看其他人是如何解决它的。

于 2013-10-04T15:23:46.780 回答