【问题标题】:How do you manage multiple versions of the same software for each customer?您如何为每位客户管理同一软件的多个版本?
【发布时间】:2010-07-07 12:02:00
【问题描述】:

我的源代码对于所有客户来说都是 95% 相同的。然而,一些客户要求一些特定的东西。我该如何管理,VisualSVN/Subversion 可以吗?

更新:

关于应用程序的一些细节,它是一个带有 NHibernate 的 Web ASP.NET MVC。

该应用程序有几个项目:web 部分、repo 部分(我们使用 NHibernate 访问数据库的地方)和一个服务项目。

服务项目使用repo项目,服务项目是有业务规则的项目。

【问题讨论】:

  • 好问题。肯定期待收到大家的来信。

标签: .net version-control visualsvn svn visualsvn-server


【解决方案1】:

我能想到两种可行的方法。

第一个涉及为每个客户分支代码。您在主线中所做的任何编辑都可以在需要时集成到特定客户的分支中。类似地,如果核心产品中的某些内容在分支中固定,则可以将其合并回主线,以便随后传播到其他客户的分支。虽然这似乎是最好的方法,但它可能很难维护和跟踪哪个分支有哪些编辑会令人担忧。

第二种,也许是更好的方法,涉及重构您的代码,以便客户特定的代码在一个程序集中 - 每个客户一个。然后在安装产品时配置它,可能通过使用依赖注入。这样你只有一个代码行,分支之间没有合并。尽管它确实依赖于轻松分离出客户特定代码。

【讨论】:

    【解决方案2】:

    将客户特定的代码放在单独的项目/程序集中。诸如策略模式或插件之类的东西可能是要走的路。

    另一种不太吸引人的方式 (IMO) 是为每个客户创建单独的分支机构,但这很快就会变得难以维护。

    【讨论】:

    • 第一句话,是的,但我更喜欢将公共代码放在库项目中,而不是插件。当然,这完全取决于具体的项目。
    • 我想建议 ig 你从 subversion 切换到分支的痛苦基本上消失了。尽管如此,我仍然会尝试将客户最想要自定义的应用程序部分分开。不过,我会推荐多个分支机构。
    【解决方案3】:

    我们采取的方法是:

    • 在应用程序中插入挂钩,允许自定义默认行为(例如,当调用 Save 操作时,内部发生的第一件事就是调用 OnSaveHandler)。
    • 默认处理程序不做任何事情,它只是返回“continueWithNormalExecution”。所有处理程序都位于与原始应用程序不同的模块中(不同的程序集),我们称之为BehaviourModule
    • 在基于客户端的请求中,我们通过覆盖默认的“不做任何事情”来修改此BehaviourModule。此修改后的处理程序的返回码可以是:ContinueNormalExecutionSkipNormalExecutionTerminateExecution 等...
    • 在其他情况下,我们会根据接口插入挂钩。在BehaviourModule 中,我们将有更多的处理程序实现这个接口,例如DoStuffInterfaceBehaviourModule 在加载时使用反射进行解析,所有实现 DoStuffInterface 的处理程序都将在系统中注册。 然后在原始应用程序中,我们将有类似:如果GetDoStuffInterfaceHandler(handlerID) isnot Nothing 然后GetDoStuffInterfaceHandler(handlerID).DoStuff()。定义要使用的 handlerId 是可配置的(可以通过 db 表、xml 文件等)。

      我们最终有多个处理程序使用不同的 ID 实现 DoStuffInterface 并在不同的时间调用它们。

    通过这种方法,我们有:

    • 具有默认行为的基本应用程序
    • 一个可配置的模块(程序集)自定义应用程序的工作方式

    这种方法的挑战在于找到“甜蜜点” - 客户可能想要自定义的行为并在其中插入钩子。

    希望我的描述清楚,如果不是......请发表评论:)

    【讨论】:

      【解决方案4】:

      如果没什么大不了的,我会使用 appp 设置和工厂模式。或每个客户的特定组件。

      但是从标签看来你想通过版本控制来解决它。但这将对合并等产生重大影响。 您必须为每个客户创建分支并将主干中的更改合并到他们。

      【讨论】:

        【解决方案5】:

        #ifdef ACME/#endif 等的一个有用附件是为 ACME_ONLY()、NON_ACME()、FROBOZCO_ONLY()、NON_FROBOZCO() 等宏定义宏。如果新版本开始发挥作用(在这种情况下,新版本的行为应该像 Acme、FrobozCo 等),事情仍然会变得混乱,但是如果 Acme 和非 Acme 版本之间只有一条线的区别,那么这种方法可以避免围绕它一行两行的#directives。

        【讨论】:

          【解决方案6】:

          5% 的区别是仅基于 UI 还是基于业务逻辑? 如果基于 UI,您应该分开 UI 层并使用应用程序发送/编译适当的 UI 文件。 如果是业务逻辑,这就更复杂了。也许分支(通过 SVN)可以提供帮助。但是对于应用程序的当前开发仍然存在问题,因此不建议这样做。

          【讨论】:

            【解决方案7】:

            使用版本控制来解决这个问题可能会导致比它解决的问题更多。

            其他人建议将特定于客户端的代码分成单独的程序集和/或使用依赖注入是一种方法。

            另一种选择是使用#if ... #endif

            #if CustomerA
            
                ... do x ...
            
            #else
            
                ... do y ...
            
            #endif
            

            您需要调整构建脚本来构建特定的客户二进制文件。例如:

            msbuild mysolution.sln /property:DefineConstants="CustomerA"
            
            msbuild mysolution.sln /property:DefineConstants="CustomerB"
            

            【讨论】:

            • 此解决方案无法很好地扩展,如果您有 5 或 10 个客户怎么办。代码的意图将在所有#ifs中丢失
            • 如果不知道 5% 是多少,就不可能说出正确的方法是什么。只是抛出另一个选择。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-13
            • 1970-01-01
            • 2013-08-03
            相关资源
            最近更新 更多