13

我们正在构建大约 10 个 ASP.NET MVC 站点,这些站点具有一组通用功能(以及相应的 URL、路由、控制器、操作和视图)。这些站点还将共享一组基础域对象(例如用户、公司)和这些对象的基础属性(例如名称、地址等)。

但每个站点也将从基础高度定制和扩展。例如,我们的大型上市公司网站将在公司域对象上具有“子公司”和“股票代码”字段,而我们的初创公司网站将具有“风险投资公司”和“资金”属性。外观和感觉也会有很大的不同,尽管我们试图保持 HTML 尽可能一致(为额外的域对象属性取模额外的表单字段等)。我们还将谨慎地覆盖图像,例如,我们可以跨站点重复使用相同的按钮图形。

无论如何,我们正在尝试找出如何最好地分解和构建事物,以便我们可以重用尽可能多的代码和尽可能多的测试,而不会限制我们添加每个应用程序属性和改变应用程序之间 UI 的自由。

我熟悉如何处理像您在 StackOverflow/SuperUser/ServerFault(或 MSDN/TechNet 中)中找到的有限定制多租户,其中 UI 略有不同,数据模型或多或少完全相同的。但是当模型和 UI 非常不同(但继承自一个共同的基础)时,我不太确定如何继续。

我不太担心操作问题,因为我们可能会在单独的应用程序域中运行每个站点并将它们托管在单独的数据库中。我更担心降低长期代码维护成本,提高敏捷性(例如,在不破坏派生应用程序的情况下轻松地向基础添加新功能),以及在我们构建第二个时实现短期开发/测试成本节约,第三,第四等网站。

我正在寻找高级指导和建议,以及如何使用现代 ASP.NET MVC 实践使该指导成为现实的具体建议。

我意识到这是一个非常笼统的问题,但对于初学者来说,我正在寻找高级指导以及如何使用 ASP.NET MVC 应用该指导的具体技巧,包括以下内容:

  • 建议在 Visual Studio 项目中拆分基础/派生的位置
  • 避免分叉的源代码控制技巧
  • 数据库模式提示(FWIW,我们的数据库都很小——每张表不到 10K 行,因此开发/测试成本比数据库性能更成问题)
  • 关于重用控制器/视图/等的提示。对应于“基本”模型属性,尤其是重新使用 UI 来处理“新客户”表单之类的内容,这些表单将混合基本属性和派生属性。

有人对如何构建这样的多租户应用程序有很好的建议吗?

4

4 回答 4

11

Here's what we do, and it works pretty well for about 8 sites currently.

  • Define a core MVC project for your Controllers, ViewModels, HttpApplication, routes, etc. This will compile into a DLL and compromise the bulk of your site.

  • Create a basic set of default views, scripts, images, etc. for your site. These will server as defaults for your individual sites.

  • Per client, create any custom controllers, routes, etc that you'll need in a project that compiles to another dll.

  • Also per client, recreate any views, scripts, images that you'll want to use.

To make the above steps work together you'll need to write a little glue. The first piece of glue is a custom view engine. You'll want to customize the standard view engine to first look for views in your client-specific folder, and then the default folder. This lets you easily override the default layout per client.

The second method of getting everything working is to have your core application load the routes, controllers, etc from your client specific assembly. To do this I use the Managed Extensibility Framework (MEF) to expose a single Register method. Calling this method on my client assembly code registers the routes and any other client-specific needs.

Here's a general view of what my site folder structure looks like, with SiteContent being checked for views first:

  - AppContent
  - AppContent/Static
  - AppContent/Static/Images
  - AppContent/Static/Scripts
  - AppContent/Static/Styles
  - AppContent/Views
  - AppContent/Views/Shared

  - SiteContent
  - SiteContent/Static
  - SiteContent/Static/Images
  - SiteContent/Static/Scripts
  - SiteContent/Static/Styles
  - SiteContent/Views
  - SiteContent/Views/Shared

  - web.config
  - Global.asax

I have helpers that I can use like SiteImage and AppImage for use in my views. Also, I make each of my client sites use certain specific names for their master pages, that I don't ever define in my AppContent defaults.

I realize this is a rough overview, but it is working well enough for us right now.

于 2010-05-10T18:30:32.267 回答
2

我目前参与了类似类型的“套件”项目,其重点是允许客户在线申请产品,但对收集哪些信息有非常相似的要求,唯一的区别在于产品特定的信息片段或略有不同不同的立法要求。

我们尝试做的一件事是创建本身可重用的页面(模型、视图和控制器组合),因此任何应用程序都可以使用该页面来捕获信息,但重定向到下一页可能会有所不同,具体取决于什么类型正在申请产品。为了实现这一点,我们以模板方法模式的形式使用抽象基础控制器,它基本上包含所有必需的控制器逻辑(包括动作方法及其应用的动作过滤器),然后使用抽象方法来完成特定的事情,例如重定向到过程中的下一页。这意味着特定应用程序页面流使用的控制器的具体实现可能仅包含一个方法,该方法返回与流中的下一页对应的 RedirectToActionResult。

还有一些包含通用功能的基本模型对象,无论是验证逻辑还是状态持久性逻辑。

在应用程序过程中捕获的数据作为 xml 序列化模型对象保存在数据库中,然后可以在应用程序完成后将其提取并反序列化,并以任何格式输出到后端操作人员用于处理应用程序的任何系统。

这意味着我们有一个项目结构,它由一个基础 dll 组成,其中包含顶级抽象类、接口和实用程序类以及 html 助手、操作过滤器等​​。然后我们有 mvc 项目,其中包含基本控制器、模型等以及视图和母版页。

最难的事情是分享观点,我认为我们还没有妥善解决这个问题。尽管使用包含区域的 MVC 2.0,我认为这将不再是一个问题,但我还没有很好地使用它。(参见 Scott Gu 在 2.0 上的帖子:http://weblogs.asp.net/scottgu/archive/2009/07/31/asp-net-mvc-v2-preview-1-released.aspx)我已经 POCed 的一件事看起来可以工作是使用基础 MVC 项目以包含公共视图,然后扩展默认视图引擎以在寻找要呈现的视图时在 Web 服务器上搜索该项目(这很容易做到)。区域虽然是一个更好的解决方案。

至于源代码控制,我们使用的是 svn,我认为您关心分支是合理的。这不是我们必须处理的事情,但我们可能会使用 git,因为它似乎使分支和合并的过程变得不那么痛苦。

不确定这是否对您有很大帮助,但我绝对建议您牢记抽象控制器和模型,并了解如何使用 html 助手和部分视图来对类似的功能进行分组。

于 2009-09-30T11:04:24.593 回答
1

一种方法是在源代码控制系统中使用分支。

主分支用于通用功能。然后,您有一个用于自定义的分支,并且可以将更改合并到自定义或返回到主分支。

于 2009-10-30T21:44:36.920 回答
1

Mike Hadlow详细介绍了如何实现这一点:

http://mikehadlow.blogspot.com/2008/11/multi-tenancy-part-1-strategy.html

于 2009-09-30T03:45:33.503 回答