在单个模型中结合“Code First”和“Database First”?

8
有没有办法在同一个上下文中结合Code-First和Database-First?当编辑EDMX文件时,我们遇到了巨大的开发时间性能问题(保存需要1.5分钟)。我已经将非插入/更新/删除UDF /存储过程移动到一些自定义T4模板中,这些模板会自动生成模型优先代码,但是当涉及EDMX时,我似乎无法让OnModelCreating被调用。
其他我们考虑过但由于某种原因不起作用的事情:
1. 我们不能(合理地)将我们的代码分成多个上下文,因为我们的实体关系存在很多重叠。此外,好像有很多人已经走过了这条路并且后悔了。 2. 我们尝试使用2个不同的上下文,但是实体与UDF之间有相当多的连接。这可能是我们的最后希望,但我真的想避免它。 3. 我们不能切换到Dapper,因为我们不幸地大量使用了IQueryable。 4. 我们试图完全转向Code-First,但是我们正在使用EDMX中不支持的功能(主要与插入/更新/删除存储过程映射相关)。

EDMX文件不会为上下文创建部分类吗?我好久没用过了... - DavidG
据我所知,如果您使用EDMX,则OnModelCreating不会被调用,但它确实存在。 - randomsolutions
我不认为这是事实? - DavidG
默认模板在 OnModelCreating 中包含以下代码:throw new UnintentionalCodeFirstException(); 因此我认为它不会被调用。 - randomsolutions
4个回答

4
请查看以下链接。我以类似的方式回答了另一个问题:
如何在使用实体框架的数据库优先方法中使用存储库模式

正如我在那篇文章中提到的,我个人会尝试切换到Code First方法,并摆脱EDMX文件,因为它已经过时,最重要的是,与Code First方法相比,维护工作量相当大且更加复杂。
从Model First方法切换到Code First并不难。下面是一些步骤和图片:
  1. 显示项目级别的所有文件并展开EDMX文件。您会注意到EDMX文件有一个.TT文件,其中将嵌套几个文件,模型上下文和POCO类之间的.cs或.vb类(取决于您使用的语言)。见下图:
    enter image description here
  2. 卸载项目,右键单击然后编辑。
  3. 请参见下图,注意上下文和TT文件之间的依赖关系
    enter image description here
  4. 删除依赖项,xml元素应如下图所示:
    enter image description here
  5. 重复模型类的过程(具有模型定义的那些类)
  6. 重新加载您的项目,删除EDMX文件
  7. 您可能需要进行一些微调和更新名称/引用。
我在过去做过几次,生产中完美运行。您还可以寻找能够为您执行此转换的工具。
这也可能是您重新考虑架构的好机会。
顺便说一句:第4个要点对您不应该是一个绊脚石。您可以通过EF映射/使用存储过程。请查看以下链接:
如何在Entity Framework 6(Code-First)中调用存储过程?

你可能还想查看仓储模式和CQRS模式。它们都有不同的优点,可能有助于满足你的需求。 - Charles

2
“似乎许多选择这种方式(多个上下文)的人后悔了。”“我不是其中之一。”“您的核心问题是上下文变得太大。因此,要将其分解。我知道必然会有应该在几个上下文中共享的实体,这可能会导致重复的类名。解决此问题的简单方法是将类重命名为其特定于上下文的名称。”“例如,我有一个ApplicationUser表(谁没有),它映射到同名类中的主要上下文,但映射到AuthorizationContext中的AuthorizationUser类,或ReportingContext中的ReportingUser类。这根本不是问题。大多数用例都围绕一个上下文类型展开,因此不可能混淆。”“我甚至有专门的上下文,以更经济的方式处理与其他上下文相同的数据。例如,一个不映射到数据库中计算列的上下文,因此插入和更新后没有读取操作(除了标识值)。所以我建议去试试,因为…”
有没有一种方法可以在同一个上下文中结合使用Code-First和Database-First?
不,没有。这两种方法都有不同的构建DbModel的方式(包含存储模型、类模型以及两者之间的映射)。在生成的DbContext中,甚至会看到抛出了UnintentionalCodeFirstException异常,以强调您不应该使用该方法。
主要涉及插入/更新/删除存储过程映射。正如另一个答案所说,将CUD操作映射到存储过程在EF6 Code-First中是受支持的。

1
我是从你在另一个问题的评论中的链接跳转过来的,那里你问道:

你提到了 code-first 和 database-first 是“技术上可行的”,能否解释一下如何实现?

首先,另一个问题的背景完全不同。那里的 OP 问的是是否可以在同一项目中同时使用 database-first 和 code-first 方法,但重要的是,并不一定是相同的上下文。我说它“技术上可行”是指前者,而不是后者。绝对没有办法在相同的上下文中同时利用 code-first 和 database-first。实际上,更具体地说,假设没有办法利用现有数据库并将该数据库与新实体迁移。
由于 EF 开发时 Microsoft 给出的一些不幸的命名,术语在这里变得有点混乱。最初,你只有 Model-first 和 Database-first。两者都使用 EDMX。唯一的区别是,Model-first 允许你设计实体并从中创建数据库,而 Database-first 则从现有数据库中创建实体。
使用EF 4.1引入了Code-first,它完全抛弃了EDMX并让您使用POCO(普通类对象)。然而,尽管有这个名字,Code-first可以并且一直能够使用现有数据库或创建新的数据库。因此,Code-first实际上是Model-first和Database-first的组合,减去了可怕的EDMX。最近,EF团队终于更进一步,完全废除了EDMX,包括Model-first和Database-first方法。目前不建议继续使用任何一个方法,并且您可以预期在未来版本的Visual Studio中将完全停止支持EDMX。
说了这么多,让我们看看事实。您无法在单个上下文中同时拥有现有数据库和由EF管理的数据库。您至少需要两个:一个用于现有表格,一个用于由EF管理的表格。更重要的是,这两个上下文必须引用不同的数据库。如果在由EF管理的数据库中有任何现有表格,则EF将尝试删除它们。长话短说,您必须将EF管理的内容与外部管理的内容分开,这意味着您无法在一个上下文和另一个上下文之间创建实体之间的外键。
你唯一的选择是采用“数据库优先”的方式。换句话说,你必须将数据库视为已存在,并手动创建新表、修改列等,完全不依赖于EF迁移。在这方面,你还应该倾倒EDMX。将所有实体生成为POCO,并在上下文中禁用数据库初始化程序。换句话说,使用现有数据库的Code-first。如果需要,我有其他信息

0

感谢大家的周到和详尽的回答。

很多其他答案都假设EF Code-First中的存储过程映射是相同的,但实际上并不是这样。我有些模糊,因为已经有6个月没有看了,但我相信自EF 6.3以来,Code First存储过程要求您将每个列从实体传递到插入/更新存储过程,并且您只需要将关键列传递给删除过程。没有选择可以传递哪些列的选项。我们需要维护谁删除了记录的要求,因此除了简单的关键信息之外,还必须传递一些其他信息。

话虽如此,我最终使用T4模板从数据库(带有一些额外的元数据)自动生成我的EDMX / Context / Model文件。这使我们的开发人员时间从1.5分钟降至约5秒。

我的希望是EF存储过程映射将得到改进,以实现与EDMX的配对,然后我就可以编写代码生成Code-First映射并完全删除EDMX生成。


1
您可以将存储过程映射到非实体和基元类型,并使用Database.SqlQuery<T>。自定义模型应与“Select”存储过程返回的列匹配。相同的思路适用于Insert、Update和Delete。我们的应用程序中使用此方法映射了多个存储过程。请查看以下链接: 使用Database.SqlQuery: https://dev59.com/7W445IYBdhLWcg3waplH MSDN文档: https://msdn.microsoft.com/en-us/library/dn468673(v=vs.113).aspx - Charles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接