【问题标题】:Entity Framework - Schema Upgrade, Multiple DBMS, and Code First实体框架 - 架构升级、多 DBMS 和代码优先
【发布时间】:2011-07-28 12:37:06
【问题描述】:

我正在考虑在即将推出的项目中使用 Microsoft 的实体框架,该项目是现有产品的单点发布。我们当前的产品支持两个 DBMS(Oracle 和 SQL Server),每个的架构都保存在单独的 .sql 脚本文件中。

实体框架 (4.1) 看起来很吸引人,因为它允许通过代码生成、反射等自动实现各种场景。但是,据我所知,其中一些好处似乎是相互排斥的。

例如,为了支持多个 DMBS,我推断我需要使用模型或代码优先设计,在这种情况下,EF 会根据模型为每个 DMBS 生成架构(我几乎没有看到任何帖子或关于此的文档,所以我可能是错的)。这意味着我们现有的模式需要被放弃(模型优先)或映射(代码优先)。此外,更新架构需要手动脚本,因为 EF 似乎不支持架构升级(不清除数据)。

  1. 模型优先和代码优先是在 EF 中支持多个 DBMS 的唯一可行方法吗?我意识到技术上不可能保证两个任意模式是相同的,所以我认为这是真的。
  2. 代码优先和映射到多个 DBMS 系统是否存在任何潜在缺陷?例如,Oracle 没有自增列;你必须使用序列。这在 DbContext 中是如何映射的?我需要为每个 DBMS 创建单独的地图吗?
  3. EF 是否支持将现有 DBMS 架构升级到代表 EF 模型的任何机制(架构重新创建 =/= 升级),还是我仅限于手动执行此操作?
  4. 我确实想出了一种可能的方法来首先使用数据库并支持多个 DBMS,但它是维护的噩梦。这个想法是为两个生成的数据模型添加另一层抽象,并为每个 EF 生成的模型创建转换器类。这似乎是最好的方法,这样每个 DBMS 都可能有自己的模型,但我的代码将处理映射。但在这样做的过程中,我真正从 EF 中获得了什么?也许是查询生成,但这值得吗?

【问题讨论】:

  • 你考虑过看看 Nhibernate 吗?
  • @Derek 我考虑过看看 Nhibernate,但这与我关于 EF 的问题没有直接关系。

标签: wcf entity-framework rdbms persistence-ignorance


【解决方案1】:

实际上,模型优先和数据库优先都有相同的约束。这两种方法都使用一个 EDMX 文件,其中包含与单个数据库提供者直接相关的 SSDL(存储 = 数据库层的描述)部分,因此如果您想拥有两个不同的数据库提供者,您必须拥有两个不同的 SSDL 部分并将它们保存在同步。您可以使用单个 CSDL(概念层的描述 = 您的模型类)和单个或两个 MSL(SSDL 和 CSDL 之间映射的描述 - 仅当表和列在两个 SSDL 中具有完全相同的名称时才可能使用单个文件)。我知道 EDMX 文件只能由单个 SSDL、CSDL 和 MSL 部分组成,所以我希望设计人员不支持这种情况,您将不得不手动修改第二个 SSDL 或使用两个 EDMX = 模型每个更改两次。

代码优先的方法可以使这变得更加简单,但问题是 Oracle 提供程序在使用代码优先和数据库生成时有多好。提供者负责在自动递增列的情况下正确解释所需的功能,例如序列。

EF 本身目前不支持升级现有数据库。使用 EDMX 时,数据库生成过程由 T4 模板或工作流控制,因此可以对其进行自定义,并且已经有一个名为 Entity Designer Database Generation Power Pack 的单独功能允许使用模型优先方法增量构建数据库。问题是此功能使用的是 VS 数据库工具。我认为这些工具只适用于 SQL 服务器。我从不喜欢这些自动化工具,所以我仍然认为应该在一些工具的帮助下手动控制数据库升级,以获取当前和上次部署的数据库版本之间的差异脚本。只有在将新版本部署到生产环境时才需要 diff 脚本。在测试和开发环境中,您始终可以重新创建整个数据库。

使用两个 EDMX 模型时应该不需要抽象。模型必须产生相同的概念层。在这种情况下,您只需要一组按约定映射的 POCO 类(与实体相同的类名,具有相同类型和可访问性的相同属性),因此它们将适用于两种模型。

编辑:

基于@Tridus 的回答,我只是补充说您可以先创建数据库并使用 EF 4.1 中的 fluentAPI 来映射它们。您的数据库必须具有完全相同的架构(表名、列名等),它们不能使用任何特定功能(我希望序列不会成为问题,因为这正是 Oracle 处理自动递增列的方式)。

【讨论】:

  • 您能否详细说明两个 EDMX 模型如何必然产生相同的概念层?如果模型代表相同的模式/关系,我可以看到生成的类可能是相同的,但是生成的类仍然是截然不同的文件(尽管可能具有相同的属性)。
  • 另外,您能否详细说明如何拥有一组具有两个映射的模型类?我知道如何首先使用代码(覆盖 OnModelCreating),但是如何使用数据库或模型优先设计来做到这一点?
【解决方案2】:

这对于数据库优先的设计实际上是相当可行的,但是由于数据库处理事情的方式不同,有些警告您将无法轻松解决。

  • 序列是一(因为它们完全被 EF 忽略)。您可以在 Oracle 中通过在插入时填充它的表上放置一个触发器来伪造它,但我还发现,如果您必须稍后更新模型,那么 EF “忘记”该列是一个标识列,它会尝试再次将 0 插入其中。如果您使用触发器,我还发现在 Oracle 中尝试获取新 ID 是不可靠的。我们只是在执行插入之前从序列中选择并在对象上设置 ID,因为这是您通常在 Oracle 中执行此操作的方式。您还可以使用存储过程来处理它。

  • 数字的处理方式不同。 SQL Server 使用映射到 Int32、Int64 等的数字格式。Oracle 的数字格式完全不同,SQL Server 中的全范围 Int32 是 Oracle 中的 Number(10,0) ...实际上是 EF 中的 Int64,因为它是比 Int32 大。我还发现 Oracle 的 EF 提供程序非常喜欢使用 Decimal,即使它不是必须的,但这可能只是一个测试版问题。

  • Oracle 中的存储过程需要将一些值放入 app.config/web.config 才能在 EF 中工作。我不确定这是否只会在 SQL Server 中造成混乱,或者是否会导致问题。

最后,EF Code First 非常不成熟,根据文档不支持更改此版本中的数据库结构。我不确定 Oracle 的供应商是否也支持它(它可能,没有尝试过)。

其中大部分是您可以解决的问题,但您需要做一些工作来隐藏与其余代码的差异,并且可能需要一个包装层来做到这一点。

编辑 - 关于您的 #4 - EF 4.1 可以生成部分 POCO 类。您可以创建另一个在更新模型时不会重新生成的部分类代码文件,然后添加隐藏差异的属性/方法,而不是围绕每个生成的模型编写包装器来隐藏任何差异。您的应用程序代码只需要注意使用它们,它们就会处理问题(就像我提到的数字问题一样,您可以使用另一个可以为 Oracle 进行必要转换的属性完全隐藏它)。

【讨论】:

  • 我需要使用 Oracle EF 提供程序(目前处于测试阶段)来了解它如何处理序列。关于您的编辑,听起来您建议有两组生成的 POCO 部分类,它们都将使用一组其他类进行扩展?如果两组 POCO 对象都在不同的命名空间中,我该怎么做?
  • 我的意思是让 Oracle EF 提供程序生成 POCO,这些 POCO 与 SQL Server POCO 位于相同的命名空间中(但与模型和 DbContext 所在的位置不同)。您可以根据要使用的数据库在项目中将它们换掉,并且代码不会知道其中的区别。在 Oracle 项目中,您可以为任何现有 POCO 添加部分类文件,以添加使其与 SQL Server 版本一致并更有效地隐藏任何差异的属性。
  • 我开始倾向于以下之一 - 1:丢弃现有的 sql 脚本并使用 POCO 在代码中重新创建整个模型。对于现有产品版本的升级,请编写执行升级的 SQL 脚本。或 2:重新创建代表现有模式的 POCO,并在 DbContext.OnModelCreating() 中手动映射它们 - 用于 SQL 和 Oracle。我将在一个单独的问题中发布这些以征求反馈。
猜你喜欢
  • 2011-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多