如何拆分Visual Studio解决方案?

25
我有一个包含40多个相互依赖的C#和C++/CLI项目的Visual Studio 2008解决方案。使用该解决方案非常缓慢,通常我只需要一次处理几个项目。因此,我决定将解决方案拆分为包含3-5个项目的多个解决方案。我也希望保留包含所有项目的“完整”解决方案(对于自动化构建或影响所有项目的大型重构操作很方便)。(这是主要条件,否则将项目拆分为多个解决方案当然很容易。)
有没有什么办法做到这一点?
我的第一个想法是创建新的空解决方案,并将一些现有项目文件添加到每个解决方案中。但如果我这样做,VS就无法再找到项目引用了(因为它们不在同一个解决方案中)。我可以将引用添加为“普通”文件引用。但如果我这样做,我的“完整”解决方案就无法工作了,因为依赖关系已丢失。
编辑:
感谢您所有人解答。我想澄清一下我的问题:我的解决方案包含44个项目,不包括测试。因此,将其分成两部分并不是我考虑的内容,我更多地考虑了分成5-8个部分。这就是为什么我想保留“完整”解决方案,让VS可以找到整个构建的正确顺序。手动维护8个单独的解决方案的构建顺序(例如在批处理文件中)对我来说似乎容易出错。
此外,我希望将项目“逻辑”分组(即我希望将通常一起修改的项目放在一个解决方案中)。但是,该分组并不总是匹配依赖关系。例如,假设我有依赖链
A is referenced by B is referenced by C is referenced by D
并且假设A和D经常一起修改,但B和C很少改变。 (显然,由B使用的A接口必须保持不变。)那么,我想在一个解决方案中拥有A和D,在另一个解决方案中拥有B和C。 但是,如果我想要从头构建所有项目,那只有完整的“完整”解决方案包含A、B、C和D才能工作。 构建完成后,我可以打开我的A / D解决方案并仅编辑/构建这两个项目。

但我担心我的问题没有优雅的解决方案。(双关语未必有意)

9个回答

12
另一种方法是考虑卸载您未使用的项目,只需右键单击并卸载您不正在使用的项目...这应该使Visual Studio更加迅速。它将许多内容保持在VS内存之外。
您可以编辑构建定义并删除任何未修改的项目(取决于您的链接方式...您知道需要/更新/不需要什么)。
解决方案->右键单击->配置管理器->取消选中任何不更改且不需要每次构建的项目。这通过使用这些项目的上一次构建输出来加快构建速度,从它们转储二进制文件的任何位置。
注意:您仍然可以右键单击项目并构建以更新其输出,然后重新构建解决方案以获取最新版本...您可以这样做,而不是来回更改构建配置以包含它。

更新
为了追求更好的性能(例如等待VS 2010的发布),您是否采取了其他一些堆栈溢出问题中列出的措施:在Visual Studio上编译时间非常慢Visual Studio优化?这些措施可以产生很大的影响,并且可能会使性能达到可接受的水平,同时我们等待VS 2010的发布
还有一些可以对性能产生良好影响的事项:

  • 如果可以选择,我强烈推荐使用SSD。它们很昂贵,但绝对值得,你的解决方案中的文件越多,它们就越有价值。当我们在工作中订购新机器时,整个开发团队都选择了SSD,差异是惊人的。当你处理一个大型解决方案时,它会将驱动器旋转并切换到数千个位置来读取文件...这就是SSD胜出的地方,寻道时间实际上是0毫秒。
  • 同样,在这方面,免费的解决方案是一个好的碎片整理程序(仅适用于物理硬盘,不要对SSD进行碎片整理),它会产生很大的影响...如果你使用一些考虑到Visual Studio的东西。我建议使用MyDefrag(免费软件),因为它的本地碎片整理算法考虑到了目录结构,所以至少物理硬盘不需要花费太多时间在切换到你解决方案中需要的文件上,它们都非常靠近磁盘。
  • 使用上述SSD建议时,请记住便宜和快速驱动器之间的SSD性能差距很大,请在这里做一些研究。不要担心你的系统,使用免费实用程序如gParted可以非常容易地将整个操作系统驱动器移动过去,你的旧驱动器仍然是一个备份...只要SSD可以容纳C盘的数据,你就没问题了。

我已经尝试过卸载(应该在问题中提到)。但是,如果我卸载一个库(用Jason的术语来说),我就不能再构建依赖它的项目了。它会因为缺少符号而出现构建错误。(可能与混合使用C++/CLI和C#代码有关) - Niki
值得注意的是,Visual Studio 2010团队在性能方面做出了巨大的改进,这可能会使您的问题成为非问题。当VS 2010发布时,您有多大机会切换到它?是否因为成本/其他团队成员而无法切换? - Nick Craver
感谢在配置管理器中添加了“取消勾选构建”功能。这一定可以节省好几年的时间! - Gleb Sevruk

10

您可以创建多个解决方案,并将任何项目添加到每个解决方案中。

您想构建的项目可能依赖于其他项目。在这种情况下,您需要从使用“项目”引用(引用解决方案中的任何其他项目)转换为使用文件引用(其中您引用该项目创建的程序集.dll)。

因此让我们将它们视为“库”(编译一次然后经常使用)和“核心”项目(您正在频繁更改)。创建包含您的“库”项目的解决方案,并(最好)添加一个后构建步骤,将生成的调试/发布dll复制到共享库文件夹中。将核心项目添加到一个新解决方案中。通过预先构建的二进制dlls(参考发布版本的dll),将所有核心解决方案引用更改为引用共享文件夹中的库。

如果更改了库,则需要构建库解决方案以重新构建它,然后使用核心解决方案重新构建依赖于该库的应用程序。(因此,最好将任何经常更改的代码放在核心解决方案中,而不是放在库中。当项目成熟时,您可以稍后移动项目,或者将库变成核心项目,如果需要长时间进行大量修改)

[2018年编辑] 现在,可以使用本地nuget或npm服务器分发库,除非有特定原因不采用此方法。

在需要较长时间构建且非常不经常更改的库的情况下,最后一种选择是将它们制作成“预编译”库-将最终的dll文件检入源代码控制,您的团队成员可以只获取最新版本并针对这些库进行构建,而无需获取或构建它们的源代码。 (要更新这些库,必须记得在构建之前检出二进制.dll文件,然后在重新构建后再次检入它们,因此您必须权衡优缺点(每天更快​​,更容易的构建)与劣势(制作更改到库需要更多的工作,源代码控制中的大型二进制文件)。


这就是我所做的。所以我猜没有办法保持“完整”的解决方案,包括所有项目? - Niki
这是问题的最佳答案,也是我曾经工作过的所有地方在解决方案变得太大时所使用的确切过程。 - MrLane
+1 同意。这仍然是我知道的最简单和有效的方法。 - mCasamento
不建议将预编译的二进制文件提交到源代码控制中。应该使用打包解决方案(例如NuGet)来管理。 - Menno Deij - van Rijswijk
公平地说,那是在2010年!是的,现在NuGet / NPM等可能是更好的库解决方案 - 回答已更新。 - Jason Williams

8

这个问题没有好的解决办法。该工具并非为此而构建。相反,您应该尽可能地将项目合并在一起。即使代码量相同,构建速度也会快很多。

您必须放弃分离项目以获得良好抽象的想法。虽然它似乎是一种非常干净的分离方式,但在这个工具链中不值得付出代价。使用语言特性,例如类、命名空间等。


叹气。虽然不是我希望得到的答案,但显然是真实的。谢谢。 - Niki
5
Jason Williams的回答应该标记为正确答案。你不应该为了构建过程而拆分或合并项目... - MrLane
@MrLane,nDepend分析会不同意你的看法。 - user585968

6
三年后,我们的团队仍然面临同样的问题——不是编译时间的问题,而是没有好的方法将逻辑上分离的内容拆分成单独的解决方案。
最终,我们建立了自己的工具soldr (开源),它是一个解决方案间构建工具。它可以选择使用nuget管理你的代码库或独立运行。我们的想法是将你的工作按照逻辑意义上的合理性分成尽可能多的解决方案(.sln)。在我们的情况下,我们为内部框架创建了一个解决方案,然后为每个后端库、每个产品等都创建了一个sln。每个解决方案都有一个“组件”目录(类似于nuget“包”目录),我们在其中存储当前解决方案正在引用的实际库文件(DLL等)。这些DLL必须从依赖项的新构建中更新,以便目标sln看到新版本。

我们不再使用手动劳动,而是使用我们之前提到的构建工具。使用非常简单的规则和约定,它自动推断解决方案之间的依赖关系。然后可以正确地构建整个依赖图(或生成MSBuild文件以执行此操作),在每个步骤中将输出构建并复制到下一个目标的组件目录中。我们还添加了过滤将要构建的功能(例如仅构建直接依赖项)、打印依赖关系图、运行单元测试等。

更新:为了利用NuGet,我们已经支持使用自动推断的依赖项生成nuspec文件。


3
我们的解决方案中有500多个项目。将所有项目放入一个解决方案中有益处,特别是当更改基础项目影响到所有其他项目时(例如,帮助类)。
我们发现继续使用Visual Studio的vsFunnel扩展是最好的方法。它有加载无项目的解决方案的能力。项目仍然列在解决方案中,但只有在需要时才会加载。当通过UI打开项目时,它就会按需加载。
真正的技巧是从vsFunnel UI启用“加载依赖项”。当您打开一个项目时,它会加载所有相关的项目等,这使得在目标项目上工作更加容易。
例如,在我们的500多个项目中,通常我们只关注单个应用程序,它可能有20个依赖项目。只有这些项目被加载,而不是全部500多个项目。
提示:选择加载依赖项和不加载任何内容-然后当您在UI中打开目标项目时,所有相关项目都将被加载。
这已经成为一个巨大的时间节省(加上SSD驱动器!)。

2
另一种选择是采用一个全面的解决方案,并创建不同的构建配置。
在窗口顶部有一个组合框,您可以选择调试或发布构建配置。下拉此选项并选择“配置管理器...”。
在“活动解决方案配置”下拉列表中,选择<New...>。创建一个新配置(例如,称为“CoreOnly”),并将“复制设置”选择为“Debug”。请取消“创建新项目配置”选项。
现在,您的“CoreOnly”构建配置将出现在窗口中。它显示了所有项目的列表。对于您不想构建的任何项目,请在右侧列中取消“Build”复选框。关闭对话框。
现在,要构建所有项目,请从配置下拉列表中选择“Debug”,并按照正常方式进行构建。当所有项目都构建完成后,您可以通过切换到CoreOnly配置来仅构建“核心”项目。只要您记得在编辑不在任何核心项目中的代码时构建Debug(所有项目)构建,您就不会有问题,并且您的CoreOnly构建速度将更快。
这种方法的缺点是解决方案打开速度可能非常慢(尽管在Visual Studio 2008Visual Studio 2010Visual Studio 2012中比Visual Studio 2005要好得多,所以您可能不会遇到问题),而且如果项目没有在您的配置中启用,它就不会被重建,因此您的构建(或运行的应用程序)可能会出错 - 如果您认为已经对禁用的项目进行了更改(或影响),则必须记住切换回常规的“构建所有”配置。

谢谢。我从未想过使用构建配置来提高构建速度,但这是一个好主意。 - Niki
是的,在VS2008中它运行良好。在VS2005中,我们有一个包含75个项目的解决方案,需要超过5分钟才能打开,但在VS2008中仅需20秒,这使得它成为更实用的方法。 - Jason Williams

2
在Visual Studio解决方案中,特别是在分组项目时,应该有关注点的分离。我最近在工作中遇到了这个问题,我不得不创建一堆单元测试,而开发人员创建的原始测试包含在解决方案中。起初,我认为好的,我会把我的测试放在这里,因为我知道它能够工作,也不必担心获取正确的依赖项。但后来,当我为每个不同的单元添加了大约20个测试项目后,我意识到它构建得非常缓慢。
然后,我决定为每组测试创建一个解决方案,而不是把它们都放在一个地方。这也有助于更好地组织您的代码,使其更容易找到。例如,我创建了一个名为“Test> Unit> MyUnitTest”的文件夹和一个名为“Test> Integration> MyIntegrationTest”的文件夹。它不仅使以后查找东西变得容易,而且还可以帮助您加快构建速度。此外,如果有很多人同时在代码上工作,每个开发人员都可以更改项目设置和配置,而不会弄乱其他人。
一般规则是将7个或更少的项目组合在一个特定区域。如果您有超过7个项目,则很可能有另一个子类别可以创建,以使其更抽象,更容易让人类大脑一目了然地理解所有复杂的细节(特别是对于新系统或者如果您在几个月甚至几年后回到项目)。

2
正是我的观点。但如果我这样做,最终会得到10-15个单独的解决方案,它们之间存在不可见的依赖关系(即必须按照完全正确的顺序构建它们),这将破坏所有自动构建脚本。 - Niki
1
然后你可以创建一个脚本按正确的顺序构建它们。我知道有一次我创建了一个解决方案并将预构建或后构建操作设置为按正确的顺序构建所有解决方案。这样,您就不必手动构建每个解决方案,而且顺序始终都是正确的。您还可以创建批处理文件或等效物,但是那样会遇到相对路径问题。如果您使用构建全部解决方案的后构建,则可以在项目设置中访问VS的相对路径变量,这非常有用。 - Brian T Hannan

2

1
除了这里提到的soldrFunnel,我可以推荐使用Solution Load Manager。是的,SSD有所帮助,但VS存在漏洞:当在100多个项目上工作时,它会崩溃、变得无响应以及出现其他问题。保持所有内容一起进行重构、重建和调试是必须的,但每天在较小的项目子集上工作将大大提高性能和满意度。
附注:我想知道是否有人对Funnel和Solution Load Manager进行了比较?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接