【问题标题】:How can I structure an ASP.NET MVC application with a "Core" database and individual derived databases using Entity Framework?如何使用实体框架构建具有“核心”数据库和各个派生数据库的 ASP.NET MVC 应用程序?
【发布时间】:2019-01-20 16:56:56
【问题描述】:

我很难为这个问题命名和措辞,因为有很多东西要解开,所以我提前道歉 - 对于任何花时间审查和回复这个问题的人,我非常感谢你。


背景:

我有一个相对较大的 ASP.NET MVC5 应用程序,使用 Entity Framework 6,使用 SQL Server 数据库。目前,该解决方案分为几个项目,主要是按层(业务、数据等)拆分。应用程序只有一个 .edmx 文件和 dbContext,它目前指向一个数据库。

上面的代码/解决方案代表了正在构建的系统的“核心”。然而,这个应用程序是为每个客户定制的,因此每个客户都可以有自己的模块、页面、逻辑等。因此,我们在解决方案中为每个客户都有一个项目(现在只有几个,但最终会是 50 + - 这是一个问题吗?也许可以拆分解决方案?)。目的是能够仅部署该客户端的代码以及核心,或者也能够仅部署核心。

除了代码中的自定义模块外,它们还可能有自己的自定义数据库,同样源自 Core 数据库。自定义数据库将始终与核心数据库保持同步,但可能有其他对象(表、存储过程等)。需要注意的是,我没有选择放弃这种方法 - 每个客户肯定会有自己的“核心”副本,但会使用内部开发的推送工具保持最新。


问题/疑问:

这样,它本质上就是核心数据库,可能会为该客户端的实现添加额外的对象。

我正在努力解决的问题是如何在 Entity Framework 中以一种不需要我将所有这些自定义 db 对象添加到 Core 数据库的方式来实现它,或者至少让它们在逻辑上分离、降级到客户项目。解决此问题的最佳方法是什么?


我的实施想法

这绝对是我目前苦苦挣扎的地方。我不确定我目前的想法是否可行,但我仍在调查并试图提出更好的选择。

我目前的想法如下......因为我可以在生成 EDMX 时以特定模式为目标,所以将客户特定对象放置在他们项目的模式中,并利用这些对象在每个客户项目/数据库中生成一个 dbContext,这继承自 Core 的 dbContext 实现(包含所有“核心”对象)。这意味着 ClientA 的项目将有一个仅包含其自定义表/对象的 edmx 文件,继承所有核心对象,但将它们与其他客户端的对象分开。

我不完全确定这种方法是否可行(现在正在使用它),我最初担心的是实体框架似乎不会在上下文之间生成外键。例如,如果 ClientA 的表具有指向核心表的外键,则生成工具似乎不会生成该关系。也就是说,我可以有效地手动实现吗?核心代码是数据库优先,但是我可以先实现较小的、客户特定的项目代码,我相信这会给我更大的灵活性。这会是一种有效的方法吗?如果没有,我可以使用更好的方法吗?

【问题讨论】:

  • 为每个客户做一个自定义数据库将是地狱。您可以为每个客户提供不同的数据库,但让他们都使用相同的设计。我的公司以这种方式拥有 7,000 个数据库。它们曾经是定制的,我听到关于它有多糟糕的恐怖故事。
  • 我遇到的问题是我不一定有一个选项——这个应用程序的每个客户端的实例都是独立的——他们有自己的数据库、Web 服务器等实例。核心代码仍然存在同样,我们部署到所有这些环境(是的,这很痛苦),但是自定义模块等也需要能够推送到各个客户端。
  • 是的,你有一个选择。停止为每个客户端做自定义数据库。您可以拥有单独的实例,而无需自定义它们。如果你走自定义路线,你会把自己置于一个痛苦的世界中。把所有需要的功能都放到标准数据库中,让每个人都使用标准数据库。使用功能切换来控制实际最终使用的功能。不要为每个客户做一个分支 - 为一个功能做一个分支。
  • 对不起,我应该更清楚一点 - 当我提到缺少选项时,我的意思更多是作为管理层的指令,因为他们不希望任何给定客户端的数据库中未使用的实体。也就是说,今天早上与他们交谈时,我能够说服他们,在客户端 B 的数据库中拥有客户端 A 的实现中未使用的实体比必须将它们作为单独的数据库进行管理要好。所以这大大简化了这一点,我仍然有一些事情需要与他们澄清,稍后可能会在这更清楚后回到 SO。
  • 干得好!不要把管理层所说的一切都视为理所当然——他们并不总是知道与他们的决策相关的成本。如果你能向他们展示为什么要以另一种方式做事的充分理由,他们会经常倾听。我的公司有三种不同类型的应用程序。我们将每个客户或设施拆分到他们自己的数据库中。一个应用程序有 7,000 个数据库,另一个有 600 个,另一个有 70 个。我们永远无法像您所说的那样管理它们。

标签: c# asp.net sql-server visual-studio entity-framework


【解决方案1】:

作为一名处于非常相似情况的开发人员(6 年的多个客户项目),我可以说你的方法充满了痛苦。为每个客户定制代码是一条通往地狱之路。

您需要为每个客户端部署相同的代码。核心保持不变。为特定客户开发的附属模块应尽可能通用(以便您可以多次转售它们)并部署给每个人。诀窍是要有一个好的切换系统,它只能为每个客户端启用正确的功能。

即有一个控制器可以保存例如公司信息。每个人都获得相同的代码,但如果客户 BobTheBuilder Ltd. 需要对公司进行特殊验证,那么该代码将进入 MyApp.BobTheBuilder.* 命名空间,并且您的配置代码应该知道应该执行此代码而不是您的通用代码。不用说,这应该通过 DI 容器来完成,并且应该通过注入实现公共接口的对象来替换实现。

至于数据库 - 您可以拥有多个代表您的数据库模块的数据库上下文。它们可以存在于同一个数据库中,但最好按模式名称分隔模块。所以是的,所有这些对象都进入您的代码库。只是不是每个租户都会获得所有表 - 只有启用的模块应该被激活并创建他们的租户表。

至于每个客户的项目 - 这也是一个很大的痛苦。想象一下,如果您有超过 10 个客户并且需要更新 Newtonsoft.Json 包 - 这通常需要比永远更长的时间!我们尝试过,然后根据客户覆盖返回到命名空间。

通常这里是我们的架构:

  • 所有租户都部署了相同的代码库,但功能被切换禁用
  • 每个租户都有自己的数据库,其中包含所有表和启用的架构(模块)
  • 不要为每个租户自定义核心。所有定制都进入模块。
  • 建议使用 CQRS,但您可以没有它。当您只需要考虑几个接口时,生活会轻松很多。
  • DI 是必须的。如果没有支持多租户的良好容器,就无法实现这一切。
  • 有些模块可以为每个客户开发一些特定的东西。每个模块都有自己的切换开关并且非常可配置 - 因此多个租户可以获得相同的模块,但可以独立重新配置。

【讨论】:

  • 感谢您的回复 - 需要注意的一点是,如果我理解正确,此应用程序在技术上不会是多租户。每个客户端都必须(在我们的案例中根据法律)拥有自己的服务器实例等。因此,如果有新客户端加入,我们需要部署核心,然后为他们配置核心。如果他们有自定义模块,那么这些模块构建在相同的解决方案中,但在该客户的项目中(因此我们可以只部署他们的代码,只部署核心,或两者),如果这有意义吗?你提出了一些很好的观点,我会尽快回复(处理工作 atm 的一些项目)
  • 此外,所有客户端都将拥有核心数据库/系统。核心是在它自己的分支中开发的(因此客户端开发永远不会直接影响核心),并且每个客户端都有自己的分支,以及仅针对他们的任何自定义模块/开发。我们只需要能够进行有针对性的部署,并且由于它们都必须拥有自己的数据库,这会使事情变得复杂,即使数据库在结构方面是相同的(加上专门为它们的实现添加的任何内容)
  • 我知道你在哪里。那是我们躲避的好时机。我们曾一度为每位客户开设一家分店。经过一定数量的合并错误后,您会发疯并失去生存意愿。有针对性的自定义部署管道也是我们做过的事情——我们幸运地留下了另一个痛苦。
  • @CTDev 并且对于要多租户的应用程序,您不必每个客户端都有一个服务器。我们也有这个——每个客户端一个服务器。没关系,但是新版本的部署仍然很痛苦。尽管使用 Octopus Deploy 显着减轻了这种痛苦。然而,每个客户端都部署了相同的代码,只是配置不同。 Octopus 在部署时处理配置。
【解决方案2】:

您可以在 ASP.NET MVC 应用程序中使用实体框架实现继承:

https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-inheritance-with-the-entity-framework-in-an-asp-net-mvc-application

有几种方法 Table-Per-Hierarchy (TPH) 继承、Table Per Type (TPT) 继承和 Table-per-Concrete Class (TPC) 继承。

如果您担心不同架构的集成方式,您也可以考虑使用微服务架构。

实体框架似乎不会在上下文之间生成外键。

这种方法听起来很痛苦。使用微服务将核心和客户端 dB 封装为它们自己的实体,然后您可以使用消息队列来代理它们之间的通信。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-09
    相关资源
    最近更新 更多