第二章:Code First概览

如果你使用第一、二版的EF框架工作过,你会回想起书中的业务案例:Break Away Geek Adventures, 简称BAGA。BAGA共享了很多像我们这样的奇客的冒险旅行。但是几年过去了,业务又在增长,到了需要更新应用程序的时候了。既然BAGA为软件奇客服务,他们不能拒绝尝试使用新技术,如EF的Code First.

在这一章里,我们从一个小例子来开始学习Code First的默认行为,然后逐步向这个例子里添加影响这种行为的信息。

我们将开始于BAGA业务域的一个小片断:包括我们旅行的目的地和我们的奇客们在这次旅行的住所。

Code First的美妙在于域类的定义代码与EF数据模型所依赖的代码是一样的。我们只需要开始于代码就可以了,例2-1,分别展示了Destination类和Loadging类。在开始的案例中,我们要保持类的简洁;这些类包含了一些自动属性,并没有什么逻辑。

Example 2-1. The domain model

public class Destination

{

public int DestinationId { get; set; }

public string Name { get; set; }

public string Country { get; set; }

public string Description { get; set; }

public byte[] Photo { get; set; }

public List<Lodging> Lodgings { get; set; }

}

public class Lodging

{

public int LodgingId { get; set; }

public string Name { get; set; }

public string Owner { get; set; }

public bool IsResort { get; set; }

public Destination Destination { get; set; }

}

Destination类描述了一个特定的BAGA旅行的目的地,对于任何给定的目的地,从Aspen到Zimbabwe,BAGA都会安排各种住所,从床位到五星级酒店,以便到时暂住。因此目的地对象要包含一个或多个居所,使用了List<lodging>来表达;

EF域类介绍

本质上,这些类与EF和Code First无关。这里只简单描述了域的一部分。

为了让EF框架能够找到这些类,需要使用EF框架的context来服务、管理和持久化数据至数据库。EF框架有两种context工具可供选择,一个是ObjectContext,这一工具从EF第一次发布就一直是EF框架的一部分,而随着EF4.1的发布,伴随Code First推出轻量级的DbContext。两种工具都可选用,但更通用(推荐)的是使用新的DbContext,也就是我们马上就要使用的。本书第7章,你将会学习如何使用ObjectContex进行Code First.

我们的BreakAwayContext类,就继承自DbContext,可以获得DbContext的所有功能。除此以外,还需要返回Destination类和Lodging类的可查询数据集DbSets,以暴露类中的属性

Example 2-2 The BreakAwayContext class

public class BreakAwayContext : DbContext

{

public DbSet<Destination> Destinations { get; set; }

public DbSet<Lodging> Lodgings { get; set; }

}

这个小小的类就代表了你在应用程序中使用的完整的数据层.感谢DbContext,你可查询,修改,跟踪和保存目的地和住所数据.下面创建一个控制台程序使用这个数据层来做一些工作,你会看到我们并非言过其实.

将片断放入程序中

为了查看有关行为,下面将带您实现一个小的VS解决方案,在此你可将这些类放进去,然后创建一个简单的控制台程序来测试你的新数据层.确保你已经开始了正确的路径,本示例将把应用程序的各个层组织在不同的项目中.

1.在VS中创建一个新的解决方案;

2.添加一个类库项目到解决方案,命名为Model.

3.在这个项目里,添加一个新的类命名为Destination.

4.修改Destination类以使其与例2-3一致:

Example 2-3. The Destination class

using System.Collections.Generic;

namespace Model

{

public class Destination

{

public int DestinationId { get; set; }

public string Name { get; set; }

public string Country { get; set; }

public string Description { get; set; }

public byte[] Photo { get; set; }

public List<Lodging> Lodgings { get; set; }

}

}

5.添加另一个类,命名为Lodging,使其与例2-4一致.

Example2-4.The Lodging class

namespace Model

{

public class Lodging

{

public int LodgingId { get; set; }

public string Name { get; set; }

public string Owner { get; set; }

public bool IsResort { get; set; }

public Destination Destination { get; set; }

}

}

这就是Model项目的内容.现在,转到数据层,因为域类尚未连接到EF框架上,我们数据层将完全依赖于EF框架.

小贴士:将NuGet安装到VS中就可以将类库方便地添加到项目中.在第1章您已学到EF可以通过NuGet获取.为了完成如下步骤,你需要为VS安装NuGet扩展.为了安装NuGet,选择工具--扩展管理….会出现扩展管理对话框,然后查找NuGet.选择NuGet扩展管理包,点击下载按钮,按照提示安装.

一旦安装完成,您就可以以一种新的方式使用VS了.你可通过VS的工具菜单--添加类库来添加所需的类库,另一种方式是使用上下文菜单来添加,方法是在解决方案的指定项目上点击右键.在下面的操作步骤中,我们使用后者

1.添加另一个类库项目,命名为DataAcess;

2.在项目资源管理器中右键单击新建立的项目选择"Add Livray Package Reference"

3.在弹出的对话框中,选择Online,并搜索Entity Framework;

4.在EF安装包上点击安装按钮,这将把Code First的运行时组件(EntityFramework.dll)添加到你的项目中;

5.右键单击新的项目并选择添加引用;

6.选择项目选项卡将Model proect加入.这将会使context能够访问刚刚在Model Project上创建的域类;

7.添加一个新类,BreakAwayContext到项目中;

8.配置这个新类如代码2-5;

Example 2-5. The BreakAwayContext class

using System.Data.Entity;

using Model;

namespace DataAccess

{

public class BreakAwayContext : DbContext

{

public DbSet<Destination> Destinations { get; set; }

public DbSet<Lodging> Lodgings { get; set; }

}

}

注意类顶部的using语句.一个是System.Data.Entity命名空间.这个命名空间使得你可以访问DbContext和DbSet类;这可能有些令人困惑,命名空间为EntityFramework.dll的程序集是 EntityFramework.dll,而不是System.Data.Entity.dll。System.Data.Entity.dll包含了EF框架的核心,已经是.Net框架的一部分.

现在你的数据访问层已经建好了.可以测试你的数据访问层了.

请等一下,我们还没有告诉数据访问层数据库在哪.还没有连接字符串,没有配置文件,没有关联的数据库.我们要在此利用Code First的第一个功能,数据库初始化.Code First有一系列的步骤来找到数据库并且初始化它.我们现在先使用默认行为,在后面第6章,将会学到更多关于数据库初始化的知识。当然,也可以开始于一个现存的数据库,只是我们现在不这么做。

1.添加一个新的控制台应用程序项目到解决方案,命名为BreakAwayConsole.

2.右键单击新的项目,设置为启动项目;

3.右键单击项目,选择添加类库引用,添加对EF框架的引用;

4.再次右键单击项目,选择添加引用,将Model和DataAcess项目添加至项目;

5.在新项目中,打开Program类;

6.添加两个using语句在文件顶部:

using Model;

using DataAccess;

7.在类中添加一个方法,命名为InsertDestination(代码2-6);

Example 2-6 The InsertDestination Methoed

private static void InsertDestination()

{

var destination = new Destination

{

Country = "Indonesia",

Description = "EcoTourism at its best in exquisite Bali",

Name = "Bali"

};

using (var context = new BreakAwayContext())

{

context.Destinations.Add(destination);

context.SaveChanges();

}

}

8.在类中调用这个方法(代码2-7)

Example 2-7. Calling the InsertDestination method

static void Main()

{

InsertDestination();

}

完成!对这两个域类你只用了少量代码,两行数据访问类以及这个小的应用类,你已经开始准备运行应用程序了。

这个控制台程序并不会显示任何信息.有趣的是发生数据库里的事情:数据库刚才并不存在。

Code First 通过检视Destination 和 Lodging 类确定了数据模型,然后根据类属性推断出数据库的架构模型.既然我们没有提供连接字符串,它就使用默认的约定,查找本地SQL Server Express实例(localhost\SQLEXPERSS)作为数据库宿主,然后以context类的类名DataAcess.BreakAwayContext匹配数据库名.没有找到这样的数据库,Code First就创建它,然后使用它在模型中发现的类依据约定创建表和表的字段。

我们来看看SQL Server Express中Code First创建的数据库(图2-1).

可以看到,DataAccess.BreakAwayContext 有三个表:Destinations ,Lodgings和EdmMetadata,后者用于Code First的数据库初始化,后面我们会提到.

关于表,构架,和字段名的约定

Code First 约定表名使用EF框架的复数化服务(由EF4引入),即使用英语语法的类名复数形式来命名表名。(这个功能可以关闭,见第7章,译者注)默认情况下,每个表都使用dbo构架创建。Code First 属性映射的列使用与类中属性一致的名字命名.

关于主键的约定

再次观察数据库表,你会找到Code First已经使用的默认规则。例如,Code First知道Detination类的DestinationId和Lodging类的LodgingId意思是键值,然后将这些键映射为数据库的主键。注意这些键都是非空主键(PK,notnull)。Code First默认约定将命名为Id或[类名]Id的属性视为类的键。DestinationId 和LodgingId显然符合这一规则。由于这些属性都是整型类型,Code First还会将它们配置为数据库的标识字段。这表示在插入数据时数据库会为这一字段自动生成值。

字符串属性的约定

字符串约定为映射到不限长度的非空列中。由数据库引擎来负责确定映射到何种类型。对于SQL Server而言,默认数据类型为nvarchar(max)。这就是你在图2-1看到的所有的字符串属性都映射成了nvarchar(max)列的缘故---Destination.Name,Country,Description等等.--同时还允许存储空值;

针对Byte数组的约定

Destination类有一个Photo属性,定义为byte[].Code First 约定byte数组映射到不限长度的非空列中.对SQL Server而言就是varbinary(max)类型.

布尔值的约定

Lodging类中的IsResort属性是一个bool类型.由于bool是一个值类型,不能分配给其一个null值.Code First强制要求此列不能为空.对SQL Server而言将bool属性映射为bit 数的库类型.

一对多关系的约定

一个目的地会有很多居所,因此Destination类有一个List<Lodging>属性允许你获取特定目的地的所有居所信息.同时,Lodging类也有一个Destination类型的属性,,因此你可看到某一居所与某一特定的目的地相连接.Code First将这种情况视为一对多关系,约定Lodging表有一个外键约束目的地与居所间的所有关系.

注意尽管在Lodging类中没有外键属性指向Destination类(如DestinationId),Code First仍然使用默认规则创建了一个,形如[Name of navigation property]_[Primary Key of related class](即Destination_DestinationId),这是Code First所为。使得EF框架知道在从数据库中查询或保存时要使用外键.

在目的地和住宿类提供给Code First的导航属性不是一个,而是两个。如果我们只提供了其中之一,关系仍然明显,Code First还是会在数据库中创建外键。还有很多提供外键的约定适用于某些场景,如多对多关系等,在后面我们会深入讨论。

使用配置来覆写约定

正如在第1章学到的,Code First允许你覆写约定,方法是添加配置.可以选择使用Data Annotation的特性标记也可以使用强类型的Fluent API来配置.

您提供的任何配置将会作为模型的一部分,EF框架使用此模型在运行时解析数据。这不仅影响数据库构架,也会影响DbContext内置的验证功能。例如,如果你告诉Code First一个属性是必备的,验证API将让你知道,如果该属性没有被填充的后果。在后续我们将看到这一行为。

Data Annotations是最简单的配置方式,直接应用到你的类和类属性上。这些特性位于System.ComponentModel.DataAnnotations命名空间,目前分布在System.ComponentModel.DataAnnotations.dll和EntityFramework.dll两个程序集中。在未来版本的.NET Framework中,EntityFramework.dll中的Annotations将迁移到System.ComponentModel.DataAnnotations.dll。所以需要在域类的项目中引用上述程序集(取决于您使用的Annotations)。注意Data Annotations能够完成常用的配置,但并非所有Code First配置都可以使用Data Annotation来完成。一些情况下只能使用另一种风格的配置方式:Fluent API。

因为Code First没有自动发现Destination和Lodging类中的某些意图,需要用一些Data Annotations提供额外的配置细节。

小贴士:在C#和Visual Basic应用特性

如果你第一次使用特性,在C#中,特性应使用方括号。例如,使用Annotation 特性Key,在c#中使用[Key],而在Visual Basic中,使用尖括号(<Key>)。当一个特性使用参数时,在C#中是表达了一个等号表达([Table(Schema="baga")]),而Visual Basic使用冒号等号表示(<Table(Schema:="baga")>) 要知道.NET代码中使用特性的更多信息,请参阅MSDN主题"应用特性",在http://msdn.microsoft.com/en-us/library/bfz783fz.aspx。

让我们从Destination类开始.关于这个类我想要作三个调整:

  • 必须提供目的地的名字
  • 限制描述字段的文本内容在500个字符以内
  • 保存照片到SQL Server数据库应为image类型,而不是varbinary(max).

上述需要的Data Annotations特性有两个包含在System.ComponentModel.DataAnnotations.dll程序集中,这是.Net 4的一部分,有一个包含在EntityFramework.dll程序集里,所以需要对两个程序集都要添加引用:

1.在Model项目中,添加对System.ComponentModel.DataAnnotations 程序集的引用.

2.通过类库包装引用添加对EntityFramework程序集的引用.

记住:你必须对每个项目都运行NuGet类库添加向导,即使类库包已经添加到解决方案中的某个项目中.使用同样的步骤将EntityFramework.dll添加到DataAccess项目中.

3.在Destination类顶部,添加System.ComponentModel.DataAnnotations命名空间.

4.修改类如代码2-8所示Example 2-8. Modified Destination class

using System.Collections.Generic;

using System.ComponentModel.DataAnnotations;

namespace Model

{

public class Destination

{

public int DestinationId { get; set; }

[Required]

public string Name { get; set; }

public string Country { get; set; }

[MaxLength(500)]

public string Description { get; set; }

[Column(TypeName = "image")]

public byte[] Photo { get; set; }

public List<Lodging> Lodgings { get; set; }

}

}

Required特性标记不需要附加信息,而MaxLength和Column特性均需要提供参数.这些参数对如何映射到数据库的列进行了明确。我们想要图片储存在SQL Server的image字段里.

小贴士:只要有可能强制将数据库中字段的类型设定为指定类型(例如将byte[]强制指定为image),你可配置数据类型特性。所有的三个特性将会影响数据库的构架,其中之二,Required和MaxLength,也被用于EF框架的验证之用.在观察效果之前,也对Lodging类作些类似的修改.

Annotation特性标记可组合使用,也就是在一个类或属性上可以附加多个annotations特性.我们准备在Lodging.Name属性附加多个配置特性.

添加下述三个注解到Lodging类的Name属性:

[Required]

[MaxLength(200)]

[MinLength(10)]

MinLenght是一个有趣的annotation特性.MaxLength在数据库有对应的含义,而MinLength并不有.MinLength将会用于EF框架的验证,并不会影响数据库.

MinLength只能通过Data Annotations来进行配置,在Fluent API 中无对应项.

理解模型变化如何影响数据库初始化

如果再次运行控制台程序,你会得到一个InvalidOperationException异常.我们刚刚对模型作的修改并没有什么错误,问题在于Code First数据库初始化的默认行为.因此我们需要在继续探索配置时需要修复这些问题.

下面是异常的信息:

The model backing the 'BreakAwayContext' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the DropCreateDatabaseIfModelChangesstrategy will automatically delete and recreate the database, and optionally seed it with new data.

自上次数据库创建以来,'BreakAwayContext'的上下文已经发生变化.要么手工删除或更新数据库,要么调用IDatabaseInitializer接口的实例 Database.SetInitializer.例如 DropCreateDatabaseIfModelChanges策略将自动删除和再创建数据库,并且可选择使用新数据作为种子.

.默认情况下,Code First只有当数据库不存在的时才创建数据库,但目前数据库已经有了,该怎么办呢?

是否还记得EdmMetadata表?如果没有,回头看一下图2-1.这个表包含了Cord First创建数据模型的部分快照。默认情况下,它会伴随着Code First第一次使用模型建立数据库时创建。然后它就会将表中的版本信息与内存中的模型相比较(这可以从EdmMetadata表中读出)。当Code First识别到上一模型的元数据模型与新建立的模型不匹配,就无法将模型映射到数据库.

您有很多选择.一个是简单删除数据库(包括其中的所有数据),然后让Code First使用默认规则(无数据库--创建新数据库)来使用更新模型创建数据库.这可能很痛苦,特别是你在开发过程中,可能会遇到某些情况导致文件锁定而无法删除.另一种方法,我们马上要用,Code First在遇到模型变化时有一套初始化策略可供使用,该行为删除数据库并重建,默认是封装在名为CreateDatabaseIfNotExists的类中,你可告知正在执行中的程序(本处是指控制台程序)使用哪个策略。

修改Main方法使之与例2-9一样.也需要在添加一个using语句,引用System.Data.Entity.

Example 2-9. Adding Database Initialization to the Main method

static void Main(string[] args)

{

Database.SetInitializer(

new DropCreateDatabaseIfModelChanges<BreakAwayContext>());

InsertDestination();

}

在此代码中我告诉Code First使用初始化器并且指定使用何种策略.(这里使用了DropCreateDatabaseIfModelChanges)应用于上下文(BrakAwayCOntext).现在当你返回应用程序,Code First将识别新旧模型的区别,通知初始化器,删除和重建数据库.

如果你已经在其他地方打开了数据库表读取数据,(例如,在Visual Studio的服务器资源管理器),Code First将不能够删除数据库。在这种情况下,在EF框架尝试删除时会有一个延迟,最终因为无法删除而抛出一个异常。似乎这样做太繁琐,特别是当我在用户组和会议展示时。我遇到的一个常见的场景是,我已打开了SQL Server Management Studio(SSMS)中,对数据库进行一些查询。你不得不关闭SSMS的数据库完全释放对数据库的锁定。

Destination类之Destinations表有三个可见的改变,Name设定为必须,在数据库表现为非空字段.Description现在是nvarchar(500)而不再是max,Photo也已经是一个image数据类型.Lodging表也得到影响,Name现在限制为200个字符且为非空型,第三个annotation:MinLength在数据库构架中没有等效值,因此在此忽略.但是,EF框架将会对此进行验证.

Data Annotations和前端验证提示

NET 4中引入的Data Annotations,可以用于ASP.NET(含.Net MVC)作为用户界面的动态数据验证工具来使用。许多Code First 使用的annotations特性标记都来自源于System.ComponentModel.DataAnnotations程序集。因此,这些数据验证功能是.Net程序集的的内置特性。

例如,"Required"特性在.NET4的程序集中,而不在EntityFramework.dll程序集里。因此,如果你在MVC应用程序中使用的Code First类,并且忽略填写一个必须输入的属性,MVC用户界面验证会响应,你可以在图2-3看到。

DbContext还提供了一个验证API,具有服务器端验证的能力,这与使用Data Annotations特性标识还是使用Fluent API来配置类是没有关系的。但是,使用Data Annotations时,MVC与特性更好地配合,实现一些验证API的功能。验证API的细节内容不包括在这本书中,本书的重点在于Code First建模,你可在另一本书:Entity Framework:DbContext找到与验证API有关的信息。

使用Fluent API 配置Code First

使用Data Annotations配置很简单,也可能是你想要的。但是Data Annotations只能获得一部分配置的功能。这时,Fluent API可以提供更多的功能,基于这个原因你可能会愿意用它.

小贴士:Fluent API是什么?

Fluent API并非专用于Code First或EF框架。Fluent API的基本思路是使用链方法调用程序代码,这样代码很容易被开发者所阅读。每一个调用的返回类型都定义为下个调用的有效方法.例如,在Code First的Fluent API中,你可以使用Entity方法选择一个实体来配置.智能感知将会显示出所可以用来配置的方法.如果你使用属性方法选择属性来配置,你就看到所有为特定属性所配置的方法.

还有另一个原因解释为什么开发者首选Fluent API。在将标记应用到你的美妙域类上时,需要定义越来越多的烦人的标记。目前还只是一些验证逻辑,当你学到更多Data Annotations配置特性的时候,你就会发现需要更多的信息指导类如何映射到数据库。如果你喜欢清洁的类,不想让类包含那么多的标记,就可以选择Fluent API。Code First的一个优势就是能够使用自己的类构建EF框架,一个充斥着数据库表名或列类型的类显然不够简洁,Fluent API解决了这一问题,我们来看这是如何实现的。

小贴士:跟随Data Annotations还是跟随Fluent API  ?

在本书后续内容中,你将会看到很多例子来说明如何配置映射,有Data Annotations的,也有使用Fluent API的.

许多映射只能通过Fluent API获得.如果你在VS中跟随我们学习,强烈推荐您创建两个单独的解决方案。否则,当使用了Data Annotation中还想看看在Fluent API中同样的配置效果,你就不得不注释掉有关代码。然后当你使用另一个Data Annotation特性标记,你又不得不注释掉Fluent API代码.在注释与取消注释之间来回调整,万一出现错误就会导致抛出异常,要么是配置重复要么是配置丢失,可能会非常麻烦。

这就意味着在创建新类或添加新方法时需要进行许多必要的复制/粘贴操作.也意味着Data Annotations 只能应用于一个解决方案而Fluent 配置被强制用于另一个.

如果你遵循此建议,还有一个附加的建议.确保在两个解决方案使用不同的命名空间。如果其中一个解决方案使用了DataLayerForAnnotations命名空间,其数据库将为DataLayerForAnnotations.BrakeAwayContext。另一解决方案使用了DataLayerForFluent,命名空间,其数据库将为DataLayerForFluent.BrakeAwayContext。你会看到有两个独立的数据库,很容易理解哪个解决方案影响了哪个数据库。

DbContext首先在类中查找可以获取的信息。这时,context已经准备好解析模型,但对开发者来说有一个机会来中断context和执行附加配置的连接.这得益于DbContext.OnModelCreating方法,这是在模型创建前被上下文所调用的方法。方法是虚拟的,因此你可以覆写并加入你自已的逻辑代码.这就Fluent API进行配置的入口.

此方法的声明方式如下:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

提供给OnModelCreating的参数DbModelBuilder是你需要添加配置的类。DbModelBuilder使用泛型和lambda表达式,所以编码是强类型的可以协助您进行配置的设置。首先要告诉DbModerBuilder对哪个类(实体)实施配置:

modelBuilder.Entity<Destination>() ;

你可以配置类映射到数据库的表名:

modelBuilder.Entity<Destination>().ToTable("a_table_name");

你也可配置类的属性.如果想要配置属性,应进一步演进:

modelBuilder.Entity<Destination>()
.Property(d => d.Description).HasMaxLength(500);

代码2-10重写了前面使用data annotations建立的配置。OnModelCrating是DbContext的一个方法,请确保它在BrakAwayContext类里。

Example 2-10. Configuring with the Fluent API

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

modelBuilder.Entity<Destination>()

.Property(d => d.Name).IsRequired();

modelBuilder.Entity<Destination>()

.Property(d => d.Description).HasMaxLength(500);

modelBuilder.Entity<Destination>()

.Property(d => d.Photo).HasColumnType("image");

modelBuilder.Entity<Lodging>()

.Property(l => l.Name).IsRequired().HasMaxLength(200);

}

如果你疑惑HasMinLength哪里去了,在Fluent配置方法里没有最小长度的配置,因为这不会对数据库的行产生影响.

除非要配合使用Data Annotations 和 Fluent API为了保持代码一致最好选择其一。为了让代码更流畅,我们已经移除了所有的Data Annotation标记.实际上Model项目也不需要对System.Component Model.DataAnnotations 或者EntityFramework程序集的引用.

再次运行代码,Code First将对比模型和数据库中的EdmMeta表,尽管我们修改了配置代码,最终结果还是一样的.同样的destination再次加入数据库中结束与匹配的记录.在图2-4你可以看到复制到的数据.

组织Fluent配置

如果你有很多配置需要执行,OnModelCreating 可能很快不堪重负(代码太多)。应该使用位于EntityTypeConfiguration的实体类来分组配置,然后在OnModelCretaing方法中告诉DbModelBuilde有些实体类r。DbModelBulider有一个Configruation属性可以来增加EntityTypeConfigurations(实体类型配置).

例2-111展示了对Destinaton类和Lodging类的分组情况:

Example 2-11. Organizing configs into separate EntityTypeConfiguration classes

using System.Data.Entity.ModelConfiguration;

using Model;

public class DestinationConfiguration :

EntityTypeConfiguration<Destination>

{

public DestinationConfiguration()

{

Property(d => d.Name).IsRequired();

Property(d => 
         .Description).HasMaxLength(500);

Property(d => d.Photo).HasColumnType("image");

}

}

public class LodgingConfiguration :

EntityTypeConfiguration<Lodging>

{

public LodgingConfiguration()

{

Property(l => l.Name).IsRequired().HasMaxLength(200);

}

}

这些代码在OnModelCreating方法内部的时候,由DbModelBuilder开始,后面跟着Entity方法来确定哪个实体进行配置。在EntityConfiguration类中,是由继承于EntityTypeConfiguration类的类开始的,这里的实体类型已经指定。例如,modelBuilder.Entity<Destination>().Property这个语句是对属性进行设置,而调用的modelBuilder.Entity<Destination>()实际上是创建了一个EntityTypeConfiguration<Destination>对象并返回给你,然后在属性上配置。所以无论使用哪种方式,访问的是同一个API。

代码2-12,你会看到修改的OnModelCrating方法,来使用这些类.

Example 2-12. Adding the configuration classes in OnModelCreating

protected override void OnModelCreating(DbModelBuilder modelBuilder)

  {

modelBuilder.Configurations.Add(new DestinationConfiguration());

modelBuilder.Configurations.Add(new LodgingConfiguration());

}

随着我们转向Fluent配置的例子,我们有时会将配置放在EntityTypeConfiguration类,有时放置在modelBulider中。这仅仅是为了让你继续看到这两种语法。但是,在最终产品中,很显然不能混用配置。你更希望有一个统一的方式,无论是将所有配置都写在OnModelCrating方法内部还是组织在EntityTypeConfiguration类中。你会看到有几个配置操作不是类型相关的,必须要直接在OnModelCreating方法中调用。

小结

本章对Code First作了基础介绍.我们看到Code First的约定规则非常智能,能够通过类的构建获知您的意图。当约定无法正确推断时,你需要使用配置明确控制Code First如何构建模型和数据库构架。我们介绍可以直接在类上采用Data Annotations的特性来实现配置。而对那些更愿意让域类独立的情形,你还学到可以使用替代的Fluent API在DbContext类中来实施配置.

Code First 可以自动为你构建数据库。你已经看到如何施加控制到其响应以对模型作出调整。期待重建数据库来匹配新的模型.现在你应该已经喜欢上了Code First,我们将进一步深入。扩展模型,学习使用Code First的来龙去脉.

Code First :使用Entity. Framework编程(2) ----转发 收藏的更多相关文章

  1. Code First :使用Entity. Framework编程(1) ----转发 收藏

    这个是在学习EF CodeFirst时发现的,对于初学者还是不错的.果断转发,方便自己以后查阅和学习. 对于学习Code First 这个教程讲解的还是很详细. 第一章:欢迎来到Code First ...

  2. Code First :使用Entity. Framework编程(7) ----转发 收藏

    第7章 高级概念 The Code First modeling functionality that you have seen so far should be enough to get you ...

  3. Code First :使用Entity. Framework编程(8) ----转发 收藏

    第8章 Code First将走向哪里? So far, this book has covered all of the Code First components that reached the ...

  4. Code First :使用Entity. Framework编程(6) ----转发 收藏

    Chapter6 Controlling Database Location,Creation Process, and Seed Data 第6章 控制数据库位置,创建过程和种子数据 In prev ...

  5. Code First :使用Entity. Framework编程(5) ----转发 收藏

    第五章 对数据库映射使用默认规则与配置 到目前为止我们已经领略了Code First的默认规则与配置对属性.类间关系的影响.在这两个领域内,Code First不仅影响模型也影响数据库.在这一章,你将 ...

  6. Code First :使用Entity. Framework编程(3) ----转发 收藏

    第三章 对属性使用约定和配置 在第2章,对Code First的约定以及如何通过配置覆写默认约定行为进行了大致的介绍.学习了如何使用Data Annotations进行配置,也学习了如何使用Fluen ...

  7. Code First :使用Entity. Framework编程(4) ----转发 收藏

    第4章 对关系使用默认规则与配置 在第3章,你已经掌握了默认规则与配置对属性以及其在数据库映射的字段的影响.在本章,我们把焦点放在类之间的关系上面.这包括类在内存如何关联,还有数据库中的外键维持等.你 ...

  8. Code First配合Entity Framework Power Tools Beta 4使用

    基于现有数据库生成POCO数据类和数据库上下文需要借助Visual Studio一个扩展插件-- Entity Framework Power Tools(一个Code First反向工程工具).只要 ...

  9. 【译著】Code First :使用Entity. Framework编程(5)

    第五章 对数据库映射使用默认规则与配置 到目前为止我们已经领略了Code First的默认规则与配置对属性.类间关系的影响.在这两个领域内,Code First不仅影响模型也影响数据库.在这一章,你将 ...

随机推荐

  1. Spring配置汇总

    现在主流的JavaWeb应用几乎都会用到Spring,以下是Spring的配置,以及结合Web的SpringMVC配置的汇总. jar包的引入 与Web项目集成 Spring配置文件 SpringMV ...

  2. SQL优化快速入门

    最近遇到一个专门进行SQL技术优化的项目,对很多既有的老存储过程进行调优(现在已经不再新增任何存储过程),因此系统的对SQL语句编写进行一次科学的学习变得很有必要.这儿将基于黄德承大神的Oracle ...

  3. Topology and Geometry in OpenCascade-Edge

    Topology and Geometry in OpenCascade-Edge eryar@163.com 摘要Abstract:本文简要介绍了几何造型中的边界表示法(BRep),并结合程序说明O ...

  4. 了解HTML表单之input元素的30个元素属性

    目录 传统属性 name type accept alt checked disabled readonly maxlength size src value 新增属性 autocomplete au ...

  5. How Do Annotations Work in Java?--转

    原文地址:https://dzone.com/articles/how-annotations-work-java Annotations have been a very important par ...

  6. Testing - 测试基础 - 探索

    定义 探索性测试(Exploratory Testing)是一种自由的软件测试风格,强调测试人员同时展开测试学习,测试设计,测试执行和测试结果评估等活动,以持续优化测试工作. 其特征有:即兴发挥,快速 ...

  7. Java多线程读取大文件

    前言 今天是五一假期第一天,按理应该是快乐玩耍的日子,但是作为一个北漂到京师的开发人员,实在难想出去那玩耍.好玩的地方比较远,近处又感觉没意思.于是乎,闲着写篇文章,总结下昨天写的程序吧. 昨天下午朋 ...

  8. java并发编程:并发容器之CopyOnWriteArrayList(转)

    原文:http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开大家都在共享同一个内容,当某个 ...

  9. JS包装对象

    一.包装对象 var s = "hello word"; s.len = 4; var t = s.len; //=>undefined 原因由于s是一个字符串,在执行第二行 ...

  10. ReflectionHelper

    public static T GetInstance<T>(Assembly assembly, string fullNamespace) { return (T)assembly.C ...