数据点

揭开 Entity Framework 策略的面纱:模型创建工作流

Julie Lerman

Julie Lerman
作为一项旨在满足各种开发风格需要的数据访问技术,Microsoft Entity Framework 为开发者提供了多个选择。一些选择需要在开发周期的早期进行,并且,它们会影响开发过程后期将进一步出现的选择。在接下来的三个专栏中,我将就您需要在 Entity Framework 中进行的一些最重要决策提供高级指导:

  • 创建概念模型的代码优先、模型优先或数据库优先工作流
  • 延迟加载、显式加载或预先加载
  • Plain Old C# Object (POCO) 或 EntityObject
  • LINQ to Entities 或实体 SQL

在应用程序中,还需要做出更多设计决策,不过,这些问题都是开发者在现有或新应用程序中开始实现 Entity Framework 时最常见的和经常讨论的。

在这里,我将重点讲述概念模型创建工作流,并在下面的两个专栏中讲述其他三个主题。我的目标是提供高度概述。

创建概念模型的三个选择

Entity Framework 依赖一种域实体概念模型,叫做实体数据模型 (EDM),而选择如何创建该模型是您需要做出的第一个决策。有三种不同的工作流可供选择:

  • 数据库优先工作流从旧数据库开始,利用向导将该数据库反向工程为概念模型。
  • 模型优先工作流从空平板开始。您使用可视 EDM 设计器设计 EDM,然后从该模型生成数据库架构。
  • 代码优先工作流从描述概念模型的类开始。没有用于代码优先的可视模型。

数据库优先:从旧数据库开始

在第一次迭代 Entity Framework 时,我们并没有提到的那么多选项。创建模型的唯一方法是将现有数据库反向工程到 EDM 中,我们将其称为数据库优先建模(参见图 1)。

利用数据库优先,从旧数据库反向工程到模型

图 1 利用数据库优先,从旧数据库反向工程到模型

事实上,您很可能看过多次这种演示了。您需要打开 EDM 向导,指向现有数据库,选择要在模型中表示的表、视图、存储过程和用户定义的函数,然后单击“完成”按钮。此时将会立即生成一个模型。数据库优先模型最初是数据库(或所选数据库的子集)的虚拟反射。无需进一步的工作,开发者就可以从 Entity Framework 获益了。您可以编写对此模型的强类型化查询,并且 Entity Framework 将为您执行这些查询,并利用结果具体化强类型化的对象。然后,在您处理结果时,Entity Framework 将跟踪更改,并且您只需调用其 SaveChanges 命令就可以将更改保存回数据库中。

这是某些开发者需要利用 Entity Framework 执行的所有工作,但他们没有注意到使用此模型的一个重要好处,即您可以使其看起来比数据库更像您的域(定义应用程序的类和关系),并且不必考虑如何从此处返回到数据库(Entity Framework 将继续自动执行此工作)。模型自定义是 EDM 的一项重要功能,但许多开发者却忽略了这一点,并因此未能从中获益。您可以将继承层次结构引入模型、重塑实体、合并和拆分实体等。

 借助数据库优先,您可以充分利用两方面的优点:利用现有数据库,这样在创建模型方面从一开始就处于远远领先的地位;然后自定义该模型以更好地反映您的应用程序域。

模型优先:从可视模型开始

您不会总有可以从其开始的旧数据库。通过模型优先,您可以直接在设计器中设计模型,然后根据该模型创建数据库架构。您可以直接在设计器中构建实体及其属性、定义关系及其约束以及创建继承层次结构。您可以指定哪些属性将成为标识键,甚至可以指定是否由数据库负责生成属性值(参见图 2)。

利用模型优先,设计用于生成数据库架构的模型

图 2 利用模型优先,设计用于生成数据库架构的模型

EDM 设计器的“从模型创建数据库”功能不会实际创建数据库。它执行的工作是构建 SQL,SQL 在执行时,将定义数据库的架构。此 SQL 称为数据定义语言,即 DDL。

还需要注意一些不太明显的事项。模型优先是在 Visual Studio 2010 和 Microsoft .NET Framework 4 中引入的。在 Visual Studio 2008 中找不到“创建数据库”选项。另外,由于这是一项新功能,您需要确保所使用的 ADO.NET 数据提供程序(例如 System.Data.Sql)已更新为提供此功能。Microsoft SQL Server 提供程序已针对 .NET 4 版本进行了更新;一些第三方提供程序也已进行更新。

默认情况下,模型优先不会对数据库执行增量架构更新,应该针对此情况做好准备。当您运行此功能时,它将创建一个全新的 DDL 脚本,该脚本将删除并随后重新创建所有数据库对象。您可以使用外部工具协助修改数据库架构而非覆盖它。否则,当您要修改模型并重新创建数据库架构时,您将希望提前备份数据或编写脚本来重新生成测试数据。

Visual Studio 在生成数据库的过程中提供了扩展点,借助 Microsoft 的 Entity Designer Database Generation Power Pack 可以利用该扩展点。您可以从 Visual Studio 2010 扩展管理器或 visualstudiogallery.com 下载该工具。此扩展不仅为您提供对使用模型优先的数据库进行增量更改的方法,还使您可以更改默认的“每个层次结构一张表”继承映射,甚至创建您的自定义 DDL 生成规则。

代码优先:从代码开始并放弃物理模型

利用数据库优先和模型优先,您最终得到的是 EDM 的物理表现形式以及附加的元数据。此模型的原始格式为 XML。它存储在扩展名为 EDMX 的文件中,您可以在 EDM 设计器或原始 XML 中处理它。在运行时,Entity Framework 将读取 XML,并使用表示元数据(实体、关系等)的专用类创建模型的内存中表现形式。Entity Framework 运行时将处理这些对象而非实际的 XML 文件。在下列情况下,此元数据尤其重要:Entity Framework 需要将模型查询转换为数据库查询;将数据库结果转换为实体实例;创建用于插入、更新和删除操作的数据库命令。Visual Studio 将从 XML 生成要在应用程序中使用的域类。

Entity Framework 团队提出了一种方法,在运行时不需要物理 EDMX 文件即可创建所需的元数据对象。这就是 Entity Framework 的第三个模型创建工作流背后的强大功能,称为代码优先。利用代码优先,不用创建 EDM,就可以像处理任何其他 .NET 开发那样创建域类。在运行时,Entity Framework 将检查这些类,然后使用一组默认约定,构建 Entity Framework 运行时可以处理的内存中模型。由于类并不总是自动提供 Entity Framework 创建该模型所需的信息,因此您可以提供其他配置(使用声明性属性/数据批注或在代码中使用 Fluent API)来进一步描述模型,从而重写前面所述的约定。您可以将配置用于多种模型任务,从标识与代码优先约定不匹配的主键到优化关系甚至指定表示继承层次结构的方式。

代码优先与模型优先类似,默认假定您不是从旧数据库开始的,因而提供从推断的模型创建数据库的功能。与模型优先不同的是,您既可以创建数据库文件,也可以创建架构。利用代码优先,当您修改模型并重新生成数据库时,仍然缺少保存数据库数据的方法。不过,代码优先支持检测模型和数据库的差异,以及使用数据库初始值设定项将数据植入数据库。

您也可以将代码优先用于现有数据库。如果类和属性的名称与数据库不匹配,您可以通过 Fluent API 添加数据批注或配置来解决此问题。图 3 显示了一个类,即使类和字段不完全匹配,该类的属性也会强制代码优先,以便映射到正确的表和属性名称。

图 3 具有代码优先属性的类,用于确保类可以正确映射到现有表

[Table("SalesOrderDetail", SchemaName="SalesLT")]
  public partial class Detail
  {
    // Scalar properties

    [Column(Name = "SalesOrderID")]
    public int OrderId { get; set; }
    [Column(Name = "SalesOrderDetailID")]
    public int DetailId { get; set; }
    public short OrderQty { get; set; }
    public int ProductId { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal UnitPriceDiscount { get; set; }

    public decimal LineTotal { get; set; }
    public System.DateTime ModifiedDate { get; set; }

    // Navigation properties

    public virtual Product Product { get; set; }
    public virtual Order Order { get; set; }
  }

决策树

Entity Framework 已发展为支持各种开发风格,现在这迫使我们必须熟悉开发人员的各种选择,以选择正确的方法。 如果要在模型中使用 UI,无论您是从现有数据库开始还是使用模型优先选项,都可以使用 EDM 设计器。 甚至在这里,您仍然可以使用建模程序来协助执行开创性的工作,然后使用模板创建代码优先类以及删除可视模型。 如果您熟悉代码并且不愿意受制于 EDMX 文件或可视建模程序,则您可能倾向代码优先(参见图 4)。

决策树

图 4 决策树

不走弯路

无论选择哪种建模技术(数据库优先、模型优先或代码优先),一旦创建了模型,当您通过模型类使用 Entity Framework 运行时开始查询、交互和保存实体时,您不会感觉到任何差异。 您可以从一个工作流开始,然后切换到其他工作流。 您可以使用 DbContext 代码生成模板从 EDMX 创建代码优先类。 您也可以从代码优先类创建 EDMX,虽然这不太容易。

我提供了一个高级选择视图以及优先选择某种方式的原因,希望我能为您揭密其中一些选择。

在以后的专栏中,我将讨论您在 Entity Framework 应用程序中将会面临的其他重大决策,我在本专栏开头已经提及:为 EntityObject 或 POCO 选择代码生成策略;使用预先加载、延迟加载或显式加载来加载相关数据;使用 LINQ to Entities 或实体 SQL 编写查询。

Julie Lerman是 Microsoft MVP、.NET 导师和顾问,住在佛蒙特州的山区。您可以在全球的用户组和会议中看到她对数据访问和其他 Microsoft .NET 主题的演示。她是受到广泛称赞的《Programming Entity Framework》(O'Reilly Media,2010)一书的作者,她的博客地址是 thedatafarm.com/blog。请关注她的 Twitter:twitter.com/julielerman

衷心感谢以下技术专家对本文的审阅:Tim Laverty