如何为每个客户管理同一软件的多个版本?
我的源代码对于所有客户来说都是 95% 相同的。然而,有些客户要求一些特定的东西。我该如何管理这个,可以使用 VisualSVN/Subversion 吗?
更新:
有关该应用程序的一些详细信息,它是一个带有 NHibernate 的 Web ASP.NET MVC。
该应用程序有几个项目:Web 部分、repo 部分(我们使用 NHibernate 访问数据库)和一个服务项目。
服务项目使用repo项目,服务项目是带有业务规则的项目。
I have source code that is 95% the same for all customers. Some customers ask for something specific, however. How can I manage this, is it possible with VisualSVN/Subversion?
Update:
Some details about the application, it's a web ASP.NET MVC with NHibernate.
The application has several projects : the web part, the repo part (where we use NHibernate to access database) and a service project.
The service project uses the repo project and the service project is the project with business rules.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我可以想到两种可能有效的方法。
第一个涉及为每个客户分支代码。您在主线中所做的任何编辑都可以在需要时集成到特定客户的分支中。同样,如果核心产品中的某些内容在分支中得到修复,则可以将其合并回主线,以便随后传播到其他客户的分支。虽然这似乎是最好的方法,但可能很难维护和跟踪哪个分支有哪些编辑会令人担忧。
第二种也许是更好的方法,涉及重构代码,以便客户特定的代码位于单个程序集中 - 每个客户一个。然后在安装产品时进行配置(可能通过使用依赖项注入)。这样你就只有一行代码并且分支之间没有合并。尽管它确实依赖于可以轻松分离出客户特定的代码。
I can think of two approaches that might work.
The first involves branching the code for each customer. Any edit you make in the main line can then be integrated into the specific customer's branch when it's needed. Similarly if something in the core product is fixed in a branch it can be merged back into the main line for subsequent propagation to the other customers' branches. While this might seem the best approach it can be hard to maintain and keeping track of which branch has which edits will get fraught.
The second, and perhaps better approach, involves refactoring your code so that the customer specific code is in a single assembly - one per customer. This is then configured, perhaps by using dependency injection, when the product is installed. This way you only have one code line and no merging between branches. Though it does rely on the customer specific code being easily separated out.
将客户特定的代码放置在单独的项目/程序集中。像策略模式或插件之类的东西可能是正确的选择。
另一种不太有吸引力的方式(IMO)是为每个客户创建单独的分支机构,但这很快就会变得难以维护。
Place the customer specific code in separate projects/assemblies. Something like the strategy pattern or plug-ins might be the way to go.
The other less attractive way (IMO) would be to create separate branches for each customer but this can quickly become hard to maintain.
我们采取的方法是:
Save
操作时,内部发生的第一件事是调用OnSaveHandler)。
BehaviourModule
BehaviourModule
不要做任何行为”。这个修改后的处理程序的返回代码可以是:ContinueNormalExecution
、SkipNormalExecution
、TerminateExecution
等...在其他情况下,我们插入钩子基于接口。在
BehaviourModule
中,我们将有更多的处理程序实现此接口,例如DoStuffInterface
,BehaviourModule
在加载时使用反射进行解析,并且所有处理程序都实现< code>DoStuffInterface 将在系统中注册。那么在原始应用程序中,我们将得到如下内容:如果
GetDoStuffInterfaceHandler(handlerID) isnot Nothing
thenGetDoStuffInterfaceHandler(handlerID).DoStuff()
。定义要使用的 handlerId 是可配置的(可以通过数据库表、xml 文件等)。我们最终有多个处理程序使用不同的 ID 实现
DoStuffInterface
并在不同的时间调用它们。通过这种方法,我们拥有:
这种方法的挑战是找到“最佳点” - 客户端可能想要自定义并在那里插入挂钩的行为。
希望我的描述很清楚,如果没有......留下评论:)
The approach we have taken is:
Save
action is called the first thing that happens inside is a call toOnSaveHandler
).BehaviourModule
BehaviourModule
by overriding the default 'don't do anything behavior'. The return code of this modified handler can be:ContinueNormalExecution
,SkipNormalExecution
,TerminateExecution
, etc ...In other cases we insert hooks based on interfaces. In the
BehaviourModule
we will have more handlers implementing this interface, e.g.DoStuffInterface
, theBehaviourModule
is parsed at load time using reflection and all handlers implementingDoStuffInterface
will be register in the system.Then in the original application we will have something like: If
GetDoStuffInterfaceHandler(handlerID) isnot Nothing
thenGetDoStuffInterfaceHandler(handlerID).DoStuff()
. Defining which handlerId to use is configurable (could be through a db table, xml file ,etc).We end up having multiple handlers implementing
DoStuffInterface
with different IDs and calling them at different times.With this approach we have:
The challenge with this approach is finding the "sweet points" - behaviours that the client might want to customize and inserting hooks there.
Hope I was clear in my description, if not... leave a comment :)
如果没什么大不了的,我会选择应用程序设置和工厂模式。或每个客户的特定组件。
但从标签来看,您似乎想通过版本控制来解决它。但这将对合并等造成很大打击。
您必须为每个客户创建分支并将更改从主干合并到他们。
If it is no big deal, i would go with appp setting and factory pattern. Or specific assemblies per customer.
But from tags it looks you want to solve it via version control. But this will put a big hit on merging etc.
You will have to create branch for each customer and merge changes from trunk to them.
#ifdef ACME/#endif 等的一个有用的辅助是为 ACME_ONLY()、NON_ACME()、FROBOZCO_ONLY()、NON_FROBOZCO() 等宏定义宏。如果新版本发挥作用,事情仍然会变得混乱(在这种情况下,新版本的行为应该像 Acme、FrobozCo 等),但如果 Acme 和非 Acme 版本之间只有一行差异,这种方法可以避免围绕这一点一行两行#directives。
A useful adjunct to #ifdef ACME/#endif etc. is to define macros for ACME_ONLY(), NON_ACME(), FROBOZCO_ONLY(), NON_FROBOZCO(), etc. macros. Stuff can still get messy if new versions come into play (in which cases should the new version behave like Acme, FrobozCo, etc.) but if there's only one line of difference between the Acme and non-Acme version, this approach avoids surrounding that line by two lines of #directives.
5% 的差异是仅基于 UI 还是也基于业务逻辑?
如果基于 UI,那么您应该分离 UI 层并随应用程序传送/编译适当的 UI 文件。
如果是业务逻辑的话,这个就比较复杂了。也许分支(通过 SVN)可以有所帮助。但当前应用程序的开发仍然存在麻烦,因此不建议这样做。
The difference of 5% is that only UI based or also business logic?
If UI based than you should sperate the UI layer and ship/compile the appropiate UI file with the application.
If business logic, this is more complicated. Maybe branching (via SVN) could help out. But still a hassle with current development to the application, therefore not advised.
使用版本控制来解决这个问题可能会导致比它解决的问题更多的问题。
其他人建议将客户端特定代码分成单独的程序集和/或使用依赖项注入是一种方法。
另一种选择是使用 #if ... #endif。
您需要调整构建脚本来构建特定的客户二进制文件。例如:
Using version control to solve this problem is probably going to cause more problems than it solves.
Suggestions by others to separate the client specific code into separate assemblies and/or use dependency injection is one way.
Another option is to use #if ... #endif.
You'll need to adjust your build scripts to build the specific customer binaries. eg: