原文地址:http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx
Entity Framework 是一个位于命名空间 System.Data.Entity 中的数据访问库。NET4 对 Entity Framework 进行了大量改进。
当 Entity Framework 第一次在 .NET SP1 被引入的时候,开发人员对这个版本提交了大量的反馈,反映他们认为第一个版本不完备的地方。SQL 开发团队认真听取了这些反馈,在 .NET4 中,解决了这些问题。
在 EF4 中的重要改进包括:
- POCO 支持: 现在你可以定义实体,不需要基类或者数据持久化的标签.
- Lazy Loading 支持: 你可以在以后加载一个模型的子对象,而不需要提前加载。.
- N-Tier Support and Self-Tracking Entities: 处理跨层的实体访问或者无状态的 web 调用.
- Better SQL Generation and SPROC support: EF4 生成更好的 SQL, 包括更好地与存储过程集成
- Automatic Pluralization Support: EF4 包括自动的表支持 (例如 Categories->Category).
- Improved Testability: EF4 的对象现在可以更加方便地使用接口.
- Improved LINQ Operator Support: EF4 现在完全支持 LINQ.
Visual Studio 2010 还包括一个功能更加丰富的 EF 设计器和工具支持。在 VS2010 中的 EF 设计器既支持数据库优先 (database first) 的开发风格,从一个已经存在的数据库开始设计模型,也支持模型优先 (model first) 的开发风格,首先设计数据模型,然后通过模型来生成数据库的架构。
使用 EF 进行代码优先的开发
为了支持以设计为中心的开发流程,EF4 还更多地支持以代码为中心 (code-centric) ,我们称为代码优先的开发,代码优先的开发支持更加优美的开发流程,它允许你:
- 在不使用设计器或者定义一个 XML 映射文件的情况下进行开发。
- 允许编写简单的模型对象POCO (plain old classes),而不需要基类。
- 通过"约定优于配置",使得数据库持久层不需要任何的配置
- 也可以覆盖"约定优于配置",通过流畅的 API 来完全定制持层的映射。
EF 代码优先的开发现在通过就可以支持,现在刚刚发布 4.1 的 RC 版,你可以通过这里下载。
它与 VS2010 一起工作,你可以在任何 .NET 4 的项目中使用,包括 ASP.NET Web Forms 项目和 ASP.NET MVC 项目。
教程:使用代码优先来创建 NerdDinner
前年,我编写了一个 MVC1.0 的 教程,这个示例发布在这里和书中,这个教程创建一个称为 NerdDinner 的简单应用,可以帮助人们简单地在线组织、安排和答复晚餐。
NerdDinner 教程使用数据库优先的方式进行设计。
下面我将演示如何通过代码优先的方式来完成这个目标,使用 EF4来创建 NerdDinner 的模型层和数据库架构,然后使用 ASP.NET MVC 构建一个 CRUD 的应用。
我们将会一步一步地创建这个应用,整个项目的下载链接在文章的后面。
第一步:创建一个空的 ASP.NET MVC2 应用
我们将通过在 VS2010 中创建一个空间的 ASP.NET MVC3 应用开始,选择 File -> New Project,使用 ASP.NET MVC2 Empty Web Application 模板。
创建完成之后的项目如下:
下一步,我们将会定义我们的 NerdDinner 模型,用来表示程序中使用的数据,同时也包括集成的验证和业务规则在内的逻辑。模型是 MVC 应用的心脏。
第二步:创建模型
假定我们还没有定义数据库,我们完全从头开创建我们的 NerdDinner 应用。
我们不需要从数据库开始
在代码优先的开发流程中,我们不需要通过创建一个数据库或者架构开始,相反,我们通过编写标准的 .NET 类定义域中的对象来开始,不需要担心任何复杂的持久层逻辑。
创建模型类
NerdDinner 是一个很小的应用,我们的数据非常简单,我们希望定义和存储 "晚餐" 来代表人们将会参加的事件,我们也希望能够定义和存储 "答复"(RSVP) 来表示人们的答复。这可以用来跟踪参与的特定晚餐。
让我们创建两个类 Dinner 和 RSVP 来表示这些内容,我们将会通过在项目中增加新的类来完成。
上面的 Dinner 和 RSVP 模型类是简单和从前的 CLR 对象,通常称为 POCO,它们不需要从任何基类派生或者实现任何接口,属性抛出了类型是标准的 .NET 数据类型,没有数据持久层的标签或者代码附加在里面。
定义模型类不需要将它们绑定在特定的数据库上,特定的数据库 API 上,或者数据库架构的实现上,这是真正强大的能力,使得我们可以更加灵活地进行数据访问,允许我们关注在应用的逻辑上而不需要担心持久层的实现。同时也使得我们可以灵活地改变数据库的架构或者存储的实现,非常流畅而不需要改写任何我们的模型对象,或者访问它们的代码。
创建上下文对象来处理数据库的持久化
现在我们已经定义了我们的两个 POCO 模型类,下面我们创建一个类来来完成从数据库中获取/持久化 Dinner 和 RSVP 实例。
我们命名这个类为 NerdDinners。它派生自 DbContext 基类,有两个公共的属性,一个暴露我们的 Dinner 对象,一个暴露我们的 RSVP 对象。
上面用到的 DbContext 和 DbSet 类是 EF4 代码优先库的一部分,你需要添加对EntityFramework 程序集的引用。同时,你还需要在类文件的开始部分增加 using System.Data.Entity 命名空间的代码。
这就是所有你需要编写的!
上面的三个类包含项目中基本的模型和数据持久层所有需要的代码,甚至我们不需要配置任何额外的数据库架构映射信息,或者运行任何的工具,也不需要编辑任何的 xml 文件,也不需要通过任何的设计器来开始使用我们的类。
基于持久层映射的约定
我们不需要任何额外的代码,也不需要创建任何 xml 文件,也不需要工具来影射我们的模型类与数据库,你可能想,这怎么可能呢?
默认情况下,EF 代码优先支持约定优于配置的目标,你可以通过映射约定来代替显式的配置参数,如果需要的话,你可以提供自定义的映射规则来覆盖默认的映射规则,但是,如果你使用默认的映射规则,你会发现你需要编写的代码真的非常少,90% 的情况下,你不需要任何额外的代码或者配置。
在我们上面的例子中,NerdDinners 上下文类将会默认的映射它的 Dinners 和 RSVPs 属性到数据库中的 Dinners 和 RSVPs 表,在 Dinners 表中的每一行将会映射到 Dinner 类的一个对象,与此类似,RSVP s 中的每一行将会映射到 RSVP 类的一个对象,Dinner 和 RSVP 类中的属性将会映射到 Disnners 和 RSVPs 表的列。
EF 支持的其他的约定规则包括基于通常的命名模式自动识别主键和外键,例如 Id 或者 DinnerId 属性将会被识别为主键,EF 还支持智能地识别模型对象之间的关系,EF 团队有一篇博文讨论默认的约定规则。点击这里
使用我们模型的示例
下面我们看一下常见的使用场景。
使用 LINQ 进行查询
我们使用下面的代码从数据库中查询数据。
我们也可以在 LINQ 中使用 Dinners 和 RSVPs 的关系,注意,下面的 where 子句过滤 RSVP 大于 0 的晚餐。
在上面的查询中,我们查询至少有一个答复的晚餐。
获取单个实例
我们可以使用 LINQ 的 Single() 方法,通过传递一个 Lambda 表达式来获取单个的 Dinner 实例。
另外,我们也可以通过 EF 支持的 Find() 方法来基于 ID 获取实例。
增加一个新的晚餐
下面的代码演示如何创建一个新的晚餐,然后写入数据库中,我们需要的就是 new 一个 Dinner 对象,设置属性,然后增加到 NerdDinners 上下文对象的 Dinners 属性的集合中,NerdDinner 上下文对象支持工作单元的模式,允许增加多个对象。然后,调用 SaveChanges() 方法来作为一个简单的原子事务操作持久化所有的修改到数据库中。
更新晚餐
更新对象的属性,然后保存到数据库中。
第三步:创建 ASP.NET MVC 的控制器使用我们的模型
下面我们在一个更加复杂的场景下使用我们的模型。我们使用控制器来实现发布一个晚餐的列表,并且允许用户增加新的项目。
在 Controller 文件夹上右键,选择 Add -> Controller 菜单,将我们的控制器命名为 HomeController。
然后,增加 Action 方法。
Index Action 方法获取并生成一个晚餐的列表。
Create Action 方法允许用户增加新的晚餐,当用户直接访问地址 /Home/Create 的时候,系统提供一个空白的晚餐表单以供填写。第二个 Create 方法处理 Post 请求,将晚餐保存到数据库中,如果有验证没有通过,将会向用户返回错误提示信息。
为控制器增加视图
下一步,我们增加两个视图,一个 Index ,一个 Create.
在 Action 方法上点击右键,选择 Add View 菜单,在 Add View 对话框中,我们使用强类型的视图,传递 Dinner 的 IEnumerable 列表。
然后,选择 Add, 将会创建 Index.aspx 视图文件,在其中增加下面的代码,创建一个无符号的列表来显示晚餐。
然后,增加 Create 视图,传递 Dinner 对象。
然后,在其中定义表单。
现在,我们实现了所有需要的功能,可以显示晚餐,并且增加项目。
第四步:数据库
我们已经完成了我们的代码,现在,运行一下。
但是,数据库呢?
我们还没有数据库,因为代码优先的工作方式并不需要一个已经存在的数据库。
但是,当我们实际运行我们程序的时候,我们还是需要一个数据库来保存我们的晚餐和回复对象。我们可以通过下面的两种方式之一创建数据库。
- 使用数据库的工具,手动创建和定义数据库
- 通过 EF Code-First 库,根据我们的模型自动创建。
第二种方式非常酷,我们下面使用这种方式。
在开始之前,我们先配置数据库连接串,来说明数据库的位置,我们的配置如下所示:
默认情况下,当创建一个 DbContext 类的时候,EF Code-First 库将会寻找与类名相同的数据库连接串,因为我们的类名为 NerdDinners ,所以,默认情况下,将会使用名为 NerdDinners 的数据库连接串。
SQL CE4 的优势
配合 EF Code-First 你可以使用许多种不同的数据库,例如 SQL Server, SQL Express,MySQL 等等。
SQL CE4 是一个轻量级的基于文件的数据库,免费,设置简单,可以嵌入在 ASP.NET 应用中,它支持低费用的寄宿环境,也可以容易地合并到 SQL Server 中。
在定义和生成模型层的阶段,SQL CE 可以快速和重新创建你的数据库,我们将使用 SQL CE4 来开发 NerdDinner 应用。以后,在产品发布的时候,通过改变数据库连接串,来使用 SQL Express 或者 SQL Server,不需要修改一行程序代码。
上面的连接串指向 NerdDinners.sdf 文件,使用 SQL CE4 数据库提供器,你需要安装 SQL CE4,可以通过独立安装:独立的 SQL CE 安装器 或者通过安装 WebMatrix, SQL CE4 很小,只需要几秒的时间来安装。
重要提示:上面的连接串指示在 \DataDirectory\ 文件夹下创建 NerdDinners.sdf 文件,在 ASP.NET 程序中, 就是在 App_Data 文件下,默认的 Empty ASP.NET MVC Web Application 没有创建这个目录,你可以通过在项目上右键菜单中 Add-> ASP.NET Folder -> App_Data 来创建。
自动创建数据架构
EF Code-First 支持通过模型类自动创建数据库的架构,不需要手动来完成这些操作。
如果数据库连接串指向的数据库或者文件不存在的时候,默认将会自动进行操作,你甚至不需要任何操作就可以。
在我们的项目中,按 F5 运行应用,你将会看到如下的界面。
通过调用 HomeController 中的 Index 方法,我们实例化并且访问 NerdDinners 上下文对象来获取所有的晚餐列表,因为数据库连接串指向的 NerdDinners.sdf 并不存在,EF Code-First 将会自动创建它,然后使用 NerdDinners 来创建数据库的架构。
在 VS 的资源管理中,点击 Show all Files 图标,然后点击 Refresh 按钮就可以看到这个数据库文件。
下面是数据库中的表
Dinners 表的结构如下。DinnerId 列被配置为主键,并设为标识列。
RSVP 表的结构如下,RSVPId 列为主键和标识列。
还创建了一个一对多的主外键关联,因为我们的 Dinners 有一个名为 RSVPs 的 ICollection
创建一个晚餐
点击 Create New Dinner 链接,将会导航到 Create 页面。
点击 Create 按钮,我们新的晚餐将会被保存到数据库中,多次重复之后,我们可能会看到下面的列表。
第五步:修改我们的模型
我们继续重构我们的程序,EF Code-First 包含许多不错的开发功能来协调开发数据库。
为模型增加新的属性
下面我们做一些简单的修改,增加一个名为 Country 的属性。
现在,按 F5 运行程序,将会看到下面的错误信息。
这是因为我们修改了 Dinner 类,现在我们的模型对象不再与数据表同步了,
当 EF 自动为你创建数据库的时候,默认情况下,会在数据库中增加一个 EdmMetadata 的表来跟踪自动创建的数据库架构。
当 EF 检测到你的模型对象与数据库不一致的时候就会抛出异常信息。
重新与数据库同步我们的模型类
有几种方式来同步我们的模型对和数据库
- 手动更新我们的数据库架构
- 手动删除我们的数据库文件,然后重新运行程序来重新创建
- 启用 EF Code-First 功能来允许它在修改模型之后自动更新我们的数据库
让我们看一下最后一种方式。
RecreateDatabaseIfModelChanges 功能
EF Code-First 包含一个开发时的功能支持在你修改模型类之后,自动重建你的的数据库。当启用之后,任何的模型修改都回导致 EF 自动重建数据库。不需要手工步骤。
在开始开发的时候,这是一个很有用的功能,使得你自由和灵活地重构和重新组织你的模型代码,而不需要任何手工操作来同步数据库的架构。尤其是与 SQL CE4 配合的时候更加好,因为这是基于文件的数据库,可以在几秒之内被删除和重建。这可以难以置信地加速开发流程。
启用这个功能的简单方式就是在Global.asax 文件的 Application_Start 中增加 Database.SetInitializer() 方法的调用。
现在,我们重新运行程序,不会看到错误信息,EF 已经重新创建了数据库。
在创建数据库的时候初始化数据
你可能注意到,重新创建的数据库是空的,我们丢掉了原来加入的数据。RecreateDatabaseIfModelChanges 行为没有合并我们的数据,它被设计用在开发场景下,快速、自动地为你更新数据库。
你可以在创建数据库的时候播种一些数据,下面的代码中,我创建了两个晚餐,然后加入到数据库中。
在 Database.Initializer() 中我们调用这个类。
现在,任何时候我们重新创建的数据库中,将会已经增加了两条数据。
第六步:增加验证规则
我们已经创建了一个简单的应用。
现在的问题是,我们没有任何的验证规则来保证填入的数据是正确的,下面我们解决这个问题。
使用 DataAnnotations 增加验证规则
在 ASP.NET MVC 中,验证规则通常基于模型,这使得可以在单一位置维护它们。强制在任何的控制器和视图中使用这些规则。
ASP.NET MVC2 包含使用 System.ComponetModel.DataAnnotations 库中的验证规则,这允许我们使用标签来标注验证规则。
回到 Dinner 类定义验证规则。
[Required] 验证标签标识必须提供的属性,[StringLength] 验证标签标识字符串的最大长度,[RegularExpression] 标签允许指定一个正则表达式进行验证,这是是验证一个电子邮件地址。
每一个标签都支持 ErrorMessages 属性,允许指定一个验证失败情况下的错误信息,可以使硬编码的字符串,也可以来自资源,可以方便地进行本地化。
引用一些 CSS 和 JavaScript 脚本
最后一步我们回到视图模板,增加 Site.css 的链接,还有两个脚本的链接,最后,在 form 元素呈现之前,还要增加一行调用 Html.EnableClientValidation() 的方法调用。
这些修改将会包含错误信息,并应用到客户端以及服务器端的验证上。
运行程序
让我们再次运行程序,创建一个新的晚餐,在没有填充任何内容的情况下,直接点击 Create 按钮,现在,我们的验证信息将会出现在浏览器上。
因为启用了客户端验证,所以,验证信息实时出现在页面上。
注意到标题长度大于 20 个字符的时候,StringLength 验证规则生效,而 HostedBy 将通过正则表达式验证电子邮件地址。
验证规则同时作用于客户端和服务器端,这使得我们可以简单清楚地保护我们的应用。
EF 4.1 Release Candidate 的下载地址