Code First :使用Entity. Framework编程(2)

Chapter2

Your First Look at Code First

第二章:Code First概览

If you've worked with either the first or second edition of Programming Entity Framework, you may recall the business that was the focus of the book sample—Break Away Geek Adventures, also known as BAGA. BAGA coordinates much-needed adventure travel for geeks like us. But it's been a few years, and the business is growing again, so it's time for some new apps. And since BAGA caters to software geeks, they can't resist an excuse to try out a new technology such as EF's Code First.

如果你使用第一、二版的EF框架工作过,你会回想起书中的业务案例:Break Away Geek Adventures,简称BAGABAGA调和了我们的很多为我们这样的奇客所需的冒险需求。但是几年过去了,业务又在增长,到了需要更新应用程序的时候了。既然BAGA为软件奇客服务,他们不能拒绝尝试使用新技术,如EFCode First.
In this chapter, we'll start with a small example to witness some of Code First's default behavior and then add to this example bit by bit to see how each thing we do impacts this behavior.

在这一章里,我们开如于一个小的例子来查看Code First的默认行为,然后逐步向这个例子里添加影响这种行为的信息。
We'll begin with a small slice of BAGA's business domain: the Destinations of our tripsand the Lodgings where our geek clients stay on these trips.

我们将开始于BAGA业务域的一个小片断:包括我们沙滩旅行的目的地和我们的奇客们在这次旅行的住所。
The beauty of Code First is that the code we use to define our domain classes is the same code that is used to describe the data model on which Entity Framework relies. And that's just where we will start—with the code, shown in Example 2-1, which describes the Destination and Lodging classes. For these early examples, we'll keep the classes very simple; they'll contain some auto-implemented properties and no further logic.

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

Example 2-1. The domain model

public class Destination

{

public int DestinationId { getset; }

public string Name { getset; }

public string Country { getset; }

public string Description { getset; }

public byte[] Photo { getset; }

public List Lodgings { getset; }

}

public class Lodging

{

public int LodgingId { getset; }

public string Name { getset; }

public string Owner { getset; }

public bool IsResort { getset; }

public Destination Destination { getset; }

}

The Destination class describes a particular locale where a BAGA trip might go. At any given destination, from Aspen to Zimbabwe, BAGA has made arrangements with various lodgings, from bed and breakfasts to five-star hotels, where clients will stay. So a destination object can have one or more lodgings—List—associated with it.

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

Introducing EF to the Domain Classes

EF域类介绍

On their own, these classes have nothing to do with Entity Framework or with Code First. They simply describe part of a domain.

本质上,这些我们自已的类与EFCode First无关。他们只是简描述了域的各个部分。


In order for Entity Framework be aware of these classes, we'll use an Entity Framework context to serve up, manage, and persist the data to a database. EF has two contexts to choose from, the ObjectContext, which has been part of Entity Framework since its first release, and the lighter-weight DbContext, which was introduced along with Code First in Entity Framework 4.1. While it's possible to use ObjectContext, it is more common (and recommended) to use the new DbContext with Code First, and that's what we'll be using here. Later in this book (Chapter 7), you'll learn about using ObjectCon
text with Code First.

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

Our class, BreakAwayContext, will inherit from DbContext in order to gain all of DbContext's capabilities. Additionally, it will expose properties that return queryable sets,DbSets, of Destination classes and Lodging Classes.

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

Example 2-2 The BreakAwayContext class

public class BreakAwayContext : DbContext

{

public DbSet Destinations { getset; }

public DbSet Lodgings { getset; }

}

This small class represents a complete data layer that you can use in applications. Thanks to the DbContext, you'll be able to query, change, track, and save destination and lodging data. Let's create a little console app to do some work with this data layer so you can see that this is no exaggeration.

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

Putting the Pieces Together in an Example

将片断放入程序中


To see all of this in action, the following section will walk you through creating a small solution in Visual Studio, where you'll place these classes and then create a simplistic console application to exercise your new data layer. To be sure you're starting on the right architectural path, this walkthrough will organize the layers of the application into separate projects.
1. Create a new solution in Visual Studio.
2. Add a Class Library project to the solution named Model.
3. In this project, add a new class named Destination.
4. Modify the Destination class to match the Example 2-3.

为了查看有关行为,下面将带您实现一个小的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 { getset; }

public string Name { getset; }

public string Country { getset; }

public string Description { getset; }

public byte[] Photo { getset; }

public List Lodgings { getset; }

}

}

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

Example2-4.The Lodging class

namespace Model

{

public class Lodging

{

public int LodgingId { getset; }

public string Name { getset; }

public string Owner { getset; }

public bool IsResort { getset; }

public Destination Destination { getset; }

}

}

That's the extent of the Model project. Now, on to the data layer. While the domain classes have no awareness of the Entity Framework, the data layer is completely dependent on it.

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

Installing NuGet into Visual Studio So You Can Add Libraries to Your Projects In Chapter 1 you learned that Entity Framework is available via NuGet. To complete
the following steps, you will need to have the NuGet extension for Visual Studio installed. To install NuGet, select Tools 
 Extension Manager... from the menu. The
Extension Manager dialog will be displayed: select Online Gallery from the left menu of the Extension Manager dialog and search for NuGet. Select the NuGet Package
Manager extension, click the Download button, and follow the prompts to install NuGet.
Once this is installed, you will be able to access the new feature in a few ways in Visual Studio. One is through the Visual Studio menu where Add Library Package Reference is a new item in the Tools menu. The other is through the context menu that pops up when you right click a project in solution explorer. In the following walkthrough, you'll use the latter method.

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

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

1. Add another Class Library project, named DataAccess, to the solution.
2. Right-click the new project in Solution Explorer and choose Add Library Package
Reference.
3. In the Add Library Package Reference dialog, select the Online tab and search for
Entity Framework.
4. Click the Install button for the Entity Framework package. This will add the Code
First runtime (EntityFramework.dll) to your project.
5. Right-click the new project in Solution Explorer and choose Add Reference.
6. Select the Projects tab and choose the Model project. This gives the context access to the domain classes that you just created in the Model project.
7. Add a new class file, BreakAwayContext, to this project.
8. Implement this new class as shown in Example 2-5.

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 Destinations { getset; }

public DbSet Lodgings { getset; }

}

}

Notice the using statements at the top of this class file. One is for the System.Data.Entity namespace. This is what gives you access to the DbContext and DbSet classes that are key to the BreakAwayContext class. This can be confusing because the classes in the System.Data.Entity namespace are defined in EntityFramework.dll, rather than System.Data.Entity.dll. System.Data.Entity.dll contains the core Entity Framework components and is included as part of the .NET Framework.

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


Now your data access layer for this simple demo is complete. It's time to exercise your data access with a small console application.

现在你的数据访问层已经建好了.现在可以测试你的数据访问层了.
But wait! We haven't told the data layer where the database is. There's no connection string, no configuration file, nothing related to a database. We'll take advantage of one of Code First's capabilities—database initialization. Code First has a series of steps it
follows to find a database to work with and initialize it. We'll let it use its default behavior for now, and later, in Chapter 6, you'll learn much more about database initialization features. But lest you get concerned, it is indeed possible to work with an existing database. We just won't go that route for now.

请等一下,我们还没有告诉数据访问层数据库在哪.还没有连接字符串,没有配置文件文件,没有关联的数据库.我们要在此利用Code First的第一个功能,数据库初始化.Code First有一系列的步骤来找到数据库并且初始化它.我们现在先使用默认行为,在后面第6,将会学到更多关于数据库初始化的知识.但是不用担心,也可以工作于一个现存的数据库,只是我们现在不这么做.
1. Add a new Console Application project to the solution, named BreakAwayConsole.
2. Right-click the new project in Solution Explorer and select Set as StartUp Project.
3. Right-click the new project in Solution Explorer again, choose Add Library Package Reference, and add a reference to the EntityFramework package.
4. Right-click the new project in Solution Explorer for the last time (we promise), choose Add Reference, and add a reference to the Model and DataAccess projects.
5. In the new project, open the Program class (Module in Visual Basic).
6. Add two using statements below the existing using statements at the top of the file:
using Model;
using DataAccess;
7. Add a method to the class called InsertDestination (Example 2-6).

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

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

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

4.再次右键单击项目,选择添加引用,ModelDataAcess项目添加至项目;

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();

}

You're done!

完成!
With this small amount of code you've written for the two domain classes, the two line data access class, and this little application, you are ready to roll. Run the
application.

对这两个域类你只用了少量代码,两行数据访问类以及这个小的应用类,你已经开始准备运行应用程序了.
This console application doesn't bother displaying any information to you. What's interesting is what's happened in the database—the database that did not exist a moment ago.

这个控制台程序并不会显示任何信息.有趣的是发生数据库里的事情:数据库刚才并不存在.
Code First used the information it discovered in the Destination and Lodging classes to determine the model and infer the schema of the database that these classes are persisted in. Since we provided no connection string information, it uses its default convention, which is to look in the local SQL Server Express instance (localhost
\SQLEXPRESS) for a database matching the fully qualified name of the context class —DataAccess.BreakAwayContext. Not finding one, Code First creates the database and then uses the model it discovered by convention to build the tables and columns of the database.

Code First 通过检视Destination  Lodging 类确定了数据模型,然后根据类如何在持久化推断出数据库的架构模型.既然我们没有提供连接字符串,它就使用默认的约定,查找本地SQL Server Express实例(localhost\SQLEXPERSS)作为数据库宿主,然后以context类的类名DataAcess.BreakAwayContext匹配数据库名.没有找到,Code First就创建数据库,然后使用它在模型中发现的类依据约定创建表和表的字段.
Therefore, we'll need to look in SQL Server Express for the database that Code First created (Figure 2-1).
And there it is, DataAccess.BreakAwayContext with a Destinations table and a Lodgings table. That EdmMetadata table is used by Code First's database initialization, and you'll learn more about that later.

因此,我们需要在SQL Server Express中查看Code First创建的数据库(2-1).

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

Code First :使用Entity. Framework编程(2)_第1张图片

Convention for Table, Schema, and Column Names

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

Code First's convention for table naming is to use Entity Framework's Pluralization Service (introduced in Entity Framework 4) to determine the plural of the class name
based on rules for the English language. By default, each table is created using the dbo schema.

Code First 约定表名使用EF的多元化服务(EF4引入),即使用英语语法的类名复数形式来命名表名.默认情况,每个表都使用dbo构架创建.
Code First creates the columns using the same names as the class properties they map to.

Code First 创建的字段使用与类中属性一致的名字命名.

Convention for Keys

关于主键的约定

Taking a closer look at the database tables, you can learn quite a bit already about Code First conventions. For example, Code First knew that DestinationId in the Destination class and LodgingId in the Lodging class were meant to be keys and that those keys map to Primary Key fields in the database. Notice that both of these database fields are non-nullable Primary Keys (PK, not null). Code First convention looks for fields that are named Id or [class name]Id and determines that these are meant to be keys for the classes. DestinationId and LodgingId match this convention. Because these properties are integer types, Code First has also configured them to be identity columns in the database. This means that the database will generate values for these columns during insert.
靠近观察数据库表,你给发现Code First已经使用的约定.例如,Code First知道Detination类的DestinationIdLodging类的LodgingId意思是键值,然后将这些键映射为数据库的主键.注意到这些数据的字段都是非空主键(PK,非空).Code First约定命名为Id[类名]Id的字段就意味着是类的键值.DestinationIdLodgingId显然符合这一规则.由由这些属性都是整型类型,Code First也将它们配置为数据库的标识字段.这表示在插入数据时数据库会为这一字段自动生成值.

Convention for String Properties

字符串属性的约定
The convention for strings is that they map to nullable columns with no limit on length.The database provider is responsible for determining exactly which data type is used for the column. For SQL Server, the default data type for columns used to store string properties is nvarchar(max). That's why you can see in Figure 2-1 that all of the string properties from the two classes have become nvarchar(max) columns—Destination.Name, Country, Description, etc.—and also allow null values to be stored.

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

Convention for Byte Array

针对Byte数组的约定


The Destination class has a Photo property, which is defined as a byte array (byte[]). As with string properties, Code First convention maps byte arrays to nullable columns with no limit on length. For SQL Server, this results in the varbinary(max) data type being used for the Photo column.

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


Convention for Booleans

布尔值的约定
The IsResort property in Lodging is a bool. Because bool is a value type, and cannot be assigned a null value, Code First presumes that the column should also not allow nulls.The SQL Server provider determines that bool properties map to the bit database type.
Lodging
类中的IsResort属性是一个bool类型.由于bool是一个值类型,不能分配给其一个null.Code First强制要求此列不能为空.SQL Server而言将bool属性映射为bit 数的库类型.

Convention for One-to-Many Relationships

一对多关系的约定
A destination can have many lodgings and the Destination class has a List property to allow you to access those lodgings from a particular destination. Additionally, the Lodging class has a Destination property, so that you can see what Destina
tion is associated with a particular Lodging. Code First recognizes this one-to-many relationship between Destination and Lodging and, by convention, determines that the Lodgings table will need a foreign key in order to persist its knowledge of which Destination a Lodging belongs to.

一个目的地会有很多居所,因此Destination类有一个List属性允许你访问特定目的地的所有居所信息.同时,Lodging类也有一个Destination类型的属性,,因此你可看到某一居所与某一特定的目的地相连接.Code First将这种情况视为一对多关系,约定将Lodging表需要一个外键以便约束目的地与居所间的所有关系.
Notice that although there is no foreign key property in the Lodging class pointing back to the Destination (e.g., DestinationId), Code First created a foreign key in the database using the pattern [Name of navigation property]_[Primary Key of related class] (i.e.,Destination_DestinationId). And thanks to some additional metadata that Code First built, Entity Framework will know to use that Foreign Key when querying from or saving to the database.

注意到尽管在Lodging类中没有外键属性指向Destination(DestinationId),Code First仍然使用特征创建了一个,形如[Name of navigation property]_[Primary Key of related class](Destination_DestinationId),感谢Code First,EF框架知道在从数据库中查询或保存时要使用外键.


The navigation properties in the Destination and Lodging classes provide Code First with not one, but two clues about this relationship. Had we only provided one of these, the relationship would still have been obvious to Code First, which still would have created the foreign key in the database.

在目的地和住宿类提供给Code First的导航属性不是一个,而是两个这种关系的线索。如果我们只提供了其中之一,关系仍然明显,Code First还是会在数据库中创建外键。
There are also conventions for scenarios where you've provided a foreign key, for many-to-many relationships, and more. You'll learn about these as you progress further through the book.
还有很多提供外键的约定适用于某些场景,如多对多关系等,在后面我们会深入学习这些.


Overriding Convention with Configurations

使用配置来重载约定
As you learned in Chapter 1, Code First allows you to override its conventions by supplying additional configuration. You can choose between attribute-based Data Annotations or the strongly typed Fluent API for providing this configuration.

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


Any configuration you supply will be included as part of the model that Entity Framework uses to reason about your data at runtime. This not only affects the database
schema but is also used by the built-in validation functionality of DbContext. For example, if you tell Code First that a property is required, the Validation API will let you know if that property has not been populated. You'll see this in action as we move forward.

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


Configuring with Data Annotations

采用Data Annotations配置
Data Annotations are the simplest form of configuration and are applied directly to your classes and class properties. These attributes are available in the System.ComponentModel.DataAnnotations namespace, which is currently distributed across the System.ComponentModel.DataAnnotations.dll and the EntityFramework.dll. In future versions of the .NET Framework, the annotations in EntityFramework.dll will move into System.ComponentModel.DataAnnotations.dll. You will need references to one or both of these assemblies (depending on which annotations you use) in the project that contains your domain classes. Be aware that the Data Annotations allow the most commonly used configuration to be performed but not all of the possible Code First configurations can be achieved with Data Annotations. Some can only be applied using
the alternate style of configuring your classes—the Fluent API.

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


Because Code First did not automatically discover some of my intent with the Destination and Lodging classes, let's use some Data Annotations to provide additional configuration details.

因为Code First没有自动发现目的地和住宿类中的一些我的意图,让我们用一些Data Annotations提供额外的配置细节。

Applying Attributes in C# and Visual Basic
In case you are new to using attributes, in C#, attributes are applied using square brackets. For example, the data annotation for identifying a Key property in C# is [Key], while in Visual Basic, angle brackets are used (). When an attribute uses a
named parameter, in C# it is expressed with an equals sign ([Table(Schema="baga")]),whereas Visual Basic uses a colon in front of the equals sign (). For more information on using attributes in .NET code, see the MSDN topic "Applying Attributes," at http://msdn.microsoft.com/en-us/library/
bfz783fz.aspx.

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

Let's start with the Destination class. There are three things I'd like to change about this class:
• Ensure that the Name is provided
• Limit the amount of text in the Description field to 500 characters.
• Store the Photo into a SQL Server image type, not a varbinary(max).
Some of the annotations I'll need for these configurations are in the System.ComponentModel.DataAnnotations.dll assembly that is part of .NET 4, but one will need a reference to the EntityFramework.dll assembly.
1. In the Model project, add a reference to the System.ComponentModel.DataAnnotations assembly.
2. Add a library package reference to this project for the EntityFramework assembly.

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

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

我需要的一些配置annotationsSystem.ComponentModel.DataAnnotations.dll程序集中,这是.Net 4的一部分,其中一个需要引用EntityFramework.dll程序集.

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

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

Remember, you'll have to use the Add Library Project Reference wizard for each project to which you want to add one of the NuGet packages, even if that package is already added to another project in your solution. Follow the same steps you did when adding EntityFramework.dll to the DataAccess project.

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

3. At the top of the Destination class, add a using for System.ComponentModel.DataAnnotations.
4. Modify the class adding annotations to Name, Description, and Photo, as shown in Example 2-8.

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 { getset; }

[Required]

public string Name { getset; }

public string Country { getset; }

[MaxLength(500)]

public string Description { getset; }

[Column(TypeName="image")]

public byte[] Photo { getset; }

public List Lodgings { getset; }

}

}

The Required annotation needs no additional information, whereas the MaxLength and Column have parameter information that you need to provide. The parameter provided to the Column annotation is specific to the database that you are mapping to. We want to store Photo in a SQL Server image field. As long as it's possible to coerce the type used for the property to the database data type you specify (e.g., coerce a byte[] to an image), you can configure the data type. All three annotations will impact the database schema and two of them, Required and MaxLength, will also be used by Entity Framework for validation. Before observing these effects, let's make some changes to the Lodging class as well.

Required annotation不需要附加信息,MaxLengthColumn特性均需要提供参数.这些参数指定了如何映射到数据库的列.我们想要陈图片储存在SQL Serverimage字段里.只要有可能就将强制将数据库中字段的类似设定为您指定的(例如将byte[]强制指定为image),你可配置数据类型特性.所有的三个annotation将会影响数据库的构架,其中这二,RequiredMaxLength,也被用于EF框架的验证之用.在观察效果之前,也对Lodging类作些类似的修改.


Annotations are composable, meaning that you can apply multiple annotations to a class or property. We'll do that for the Lodging.Name property.
Annotation
可组合使用,也就是在一个类或属性上可以附加多个annotations.我们准备在Lodging.Name属性采用多个注解.

Add the following three annotations to the Name property in the Lodging class:

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

[Required]

[MaxLength(200)]

[MinLength(10)]



MinLength is an interesting annotation. While MaxLength has a database counterpart,MinLength does not. MinLength will be used for Entity Framework validation, but it won't impact the database.

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

MinLength is the only configuration that can be achieved using Data Annotations but has no counterpart in the Fluent API configurations.

MinLength只能通过Data Annotations来进行配置,Fluent

API 中无对应项.

Understanding How Model Changes Impact Database

Initialization

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


If you were to run the console application again, you'd get a big fat InvalidOperationException. There's nothing wrong with the model changes that we've made here. The problem is with the default behavior of Code First database initialization. So we're going to have to stop the presses, switch gears, and fix this problem before
moving on with exploring configuration.

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


Here's the exact exception message:

下面是异常的信息


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 DropCreateDatabaseIfModelChanges
strategy will automatically delete and recreate the database, and optionally seed it with new data.

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


By default, Code First will create the database only if it doesn't already exist, but your database does exist.
默认情况,Code First只有当数据库不存在的时才创建数据库,但目前数据库已经有了.

Remember that EdmMetadata table in the database? If not, take a peek at Figure 2-1 again.That table contains a snapshot of the database section of the model that Code First built. When Code First uses a model for the first time in an application process, by default, it will go through its model building process. Then it will compare the in-memory model with the last version—which it can see by reading the EdmMetadata table.In this case, Code First recognized that the metadata of the new model does not match the metadata of the previous model and therefore cannot guarantee that your model will map to the database.

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


You have a number of options. One is to simply delete the database (and all of its data along with it) and let Code First use its rule (no database=create a new one, please) to recreate the database using the updated model. This can get to be a pain when you are in development and can also create file lock issues if you try to run your application too quickly after deleting the database. Another, which we will use for now, is to delete and recreate the database whenever a model change is detected by Code First. Code First has a set of initialization strategies to apply these rules for you. The default isencapsulated in a class called CreateDatabaseIfNotExists. The one we'll use is in a class
called DropCreateDatabaseIfModelChanges. You can tell your executing application (in this case, the console app) which strategy to use. Here's how we'll do it.
您有很多选择.一个是简单删除数据库(包括其中的所有数据),然后让Code First使用规则(无数据库--创建新数据库)来使用更新模型创建数据库.这可能很痛苦,特别是你在开发过程中,并且可能由于过快运行了程序导致文件锁定而无法删除.另一种方法,我们马上要用,就删除和重建任何被Code First所检测到的与数据库有关的内容.Code First有一套初始化策略提供给我们使用.默认是封装在名为CreateDatabaseIfNotExists的类中你可告知正在执行中的程序(本处是指控制台程序)使用哪个策略.下面的内容就介绍这一点.

Modify the Main method adding in the code shown in Example 2-9 above InsertDestination. You'll need to add a using statement for System.Data.Entity as well.

修改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());

InsertDestination();

}

In this code we're telling Code First to use an initializer and specifying which strategy to use (DropCreateDatabaseIfModelChanges) and on which context (BreakAwayContext). Now when you rerun the application, Code First will recognize the difference in the
new model and, with permission from the initializer, will delete and recreate the database when the time comes.

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

If you've opened up the database tables to read data somewhere else (for example, in Visual Studio's Server Explorer), Code First will not be able to delete the database. In this case, there will be a delay while it
attempts the delete, and then eventually EF will get the message from the database and an exception will be thrown. I seem to do this too frequently when I'm demonstrating at user groups and conferences.
A common scenario I encounter is that I have opened up SQL Server Management Studio (SSMS) and performed some queries on the database. Closing the query windows releases the database. You shouldn't have to close SSMS completely to release its clutches on the database.

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

Code First :使用Entity. Framework编程(2)_第2张图片

The three changes we made to the Destination class are now visible in the Destinations table. Name, which we set to Required, is now non-nullable in the database. Description is now an nvarchar(500) rather than max and Photo is an image data type.

Destination类之Destinations表有三个可见的改变,Name设定为必填,在数据库表现为非空字段.Description现在是nvarchar(500)而不再是max,Photo也已经是一个image数据类型.

The Lodgings table has also been affected. Name is now limited to 200 characters and is non-nullable. The third annotation applied to Name, MinLength, has no equivalent in the database schema, so it is ignored here. Remember, though, Entity Framework will pay attention to that attribute when it is validating Lodging objects.

Lodging表也得到影响,Name现在限制为200个字符且为非空型,第三个annotation:MinLength在数据库构架中没有等效,因此在此忽略.但是,EF框架将会对此进行验证.

Data Annotations and Validation-Aware UIs

UI端展示数据注解与验证

Data Annotations were introduced in .NET 4 for use in validation-aware UIs such as ASP.NET MVC and ASP.NET Dynamic Data. Many of the annotations you'll use for
your Code First classes come from this same set of annotations that live in the System.ComponentModel.DataAnnotations assembly. Therefore, those UIs will respond to invalid data where the validation is based on one of the annotations from this assembly.

NET 4中引入的数据注解,可以用于如ASP.NET MVCASP.NET的动态数据验证的用户界面。许多Code First 使用的annotations都来自于System.ComponentModel.DataAnnotations程序集的注解集.。因此,这些用户界面将响应无效数据验证是都基于此程序集的的内置特性之一。


For example, the Required annotation is in the .NET 4 assembly, not the EntityFramework.dll assembly. Therefore, if you are using your Code First classes in an MVC application and the user has neglected to fill out a field that is bound to a Required property, the MVC UI validation will respond, as you can see in Figure 2-3.

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

Code First :使用Entity. Framework编程(2)_第3张图片

DbContext also provides a Validation API, so there is also server-side validation happening, whether you configure with Data Annotations or the Fluent API. But when you are using Data Annotations that are also tracked by MVC, MVC will pick them up as well. The Validation API
is not covered in this book, which focuses on modeling with Code First,but it will be part of the partner book, Entity Framework: DbContext.

DbContext还提供了一个验证API,所以也有服务器端验证的事件,无论配置数据使用的是Data Annotations科学家是Fluent API。但是,当您使用Data Annotations,MVC也对其进行跟踪,MVC与这些注解特性配合得很好.验证API不包括在这本书中,本书的重点在于Code First建模,你可在另一本书:实体框架:DbContext找到有关信息。

Configuring Code First with the Fluent API

使用Fluent API 配置Code First


Configuring with Data Annotations is fairly simple and it may be just what you're looking for. But Data Annotations only allow you to access a subset of the possible configurations (though much more than you've seen so far). The Fluent API, however, gives you access to even more, so you may prefer it for this reason.

使用Data Annotations配置很简单也可能是你想要的.但是Annotations不允许访问一组可能的配置(尽管还没有看到).这时,Fluent API可以提供更加功能,基于这个原因你可能会愿意用它.

What Is a Fluent API?
The concept of a fluent API isn't specific to Code First or the Entity Framework. The fundamental idea behind a fluent API involves using chained method calls to produce
code that is easy for the developer to read. The return type of each call then defines the valid methods for the next call. For example, in the Code First Fluent API, you can use the Entity method to select an entity to configure. IntelliSense will then show you all the methods that can be used to configure an Entity. If you then use the Property method to select a property to configure, you will see all the methods available for
configuring that particular property.

Fluent API是什么?

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

There's another reason why some developers will prefer the Fluent API over the annotations. While applying annotations to your pretty domain classes, they can definitely get more and more bogged down with the attributes. It's one thing to be applying validation logic (Required, MaxLength, etc.), but as you learn more about configuration options, you'll see that there are also many that are specifically about how the class
maps to the database. If you prefer cleaner classes, you may not want the class to include information about what table name it should map to in the database. One of the benefits that many developers see in Code First is that it allows you to use Entity Framework with classes that are persistence-ignorant. A class that includes database table names or column data types is not at all ignorant of how it's being persisted. The Fluent API
allows you to associate the configurations with the context rather than the classes themselves. The classes remain clean. Let's see how this works.

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

Following the Data Annotations and Fluent API Walkthroughs

跟随Data Annotations还是跟随Fluent API 
Throughout the rest of this book, you will see many examples of how to configure mappings using Data Annotations and how to configure them using the Fluent API.

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


Some mappings can only be achieved with the Fluent API.
If you are following along with the examples in Visual Studio, we highly recommend that you do so in two separate solutions. Otherwise, when you code a Data Annotation and then want to see the same effect in Fluent configurations, you'll have to comment out the Data Annotation. Then, when you move forward to another annotation, you'll have to comment out the Fluent configurations. It won't take long for the jumble of
commented and uncommented code to cross wires and throw error messages that occur because of overlapping or completely missing configurations.

许多映射只能通过Fluent API获得.如果你在VS中跟随我们学习求你,我强烈推荐您在两个单独的解决方案中使用.否则,当你在Data Annotation中还想看看在Fluent同样的配置效果,你就会不得不
注释掉有关代码.然后当你进入到另一个annotation,你又不得不注释掉fluent代码.在注释与取消注释之间来自调整,万一出现错误就会导致抛出异常,这是因为配置重复或者是丢失了配置.

This will mean some required copy/pasting when we have you add a new class or add a new method to the solution. But it also means Data Annotations will only be applied in one solution and Fluent configurations will be restricted to the other.

这就意味着在创建新类或添加新方法时需要进行许多必要的复制/粘贴操作.也意味着Data Annotations 只能应用于一个解决方案而Fluent 配置只能用于另一个.
If you do follow this advice, there is one additional suggestion we have for you: Be sure to use different namespace names for the BreakAwayContext class in the two solutions.That way if one solution uses the namespace DataLayerForAnnotations, its database will
be DataLayerForAnnotations.BreakAwayContext. If the other uses DataLayerForFluent, its database name will be DataLayerForFluent.BreakAwayContext. You will be happier to have two separate databases and a clear understanding of which solution is impacting which database.

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

When it's time to build the model, the DbContext first looks at the classes and learns what it can from them. At this point, the context is ready to reason out the model, but there is an opportunity for the developer to interrupt the context and perform additional 
configuration. This is thanks to the DbContext.OnModelCreating method, which is called
by the context just before the model is built. The method is virtual, so you can override it and insert your own logic. This is where the Fluent API configurations go.

当创建模型时,DbContext首先在类中查找可以获取的信息.在这一点上,the context is ready to reason out the model(不知道怎么译---译者),但对开发者来说有一个机会来中断上下文和执行附加配置的连接.这需要感谢DbContext.OnModelCreating方法,这是在模型创建前被上下文所调用的方法.方法是虚拟的,因此你可以重写它并加入你自已的逻辑代码.这就Fluent API进行配置的入口.


The signature of this method is as follows:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

The DbModelBuilder that is provided to the OnModelCreating method is the class that allows you to add configurations. The DbModelBuilder leverages generics and lambdas so you'll get plenty of strong typing to help you along while coding the configurations.
提供给OnModelCreating的参数DbModelBuilder是你需要添加配置的类.DbModelBuilder利用泛型和lambda表达式,所以你将得到大量的强类型以帮助您编码配置。

The basic pattern is to tell the DbModelBuilder which entity (class) you want to configure:

基本特性为告诉DbModerBuilder哪个类(实体)需要进行配置:

modelBuilder.Entity()

You can configure the class itself (e.g., what database table it maps to):

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

modelBuilder.Entity().ToTable("a_table_name")

You can also configure properties of the class. If you want to configure a property, you have to drill in further:

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

modelBuilder.Entity()

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

The code in Example 2-10 replicates all of the configuration that we previously performed using the data annotations. OnModelCreating is a method of DbContext, so be sure to put it in your BreakAwayContext class.

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

Example 2-10. Configuring with the Fluent API

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

modelBuilder.Entity()

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

modelBuilder.Entity()

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

modelBuilder.Entity()

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

modelBuilder.Entity()

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

}

Some of the configurations are composable. Notice that the configuration for Lodging.Name combines IsRequired with HasMaxLength.

很多配置是可组合的.注意到Lodging.Name组合的IsrequiredHasMaxLength.

If you are wondering where HasMinLength is, there is no Fluent configuration for minimum length, as it is not a facet of a database column.

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

While it's possible to use a combination of Data Annotations and the Fluent API, it makes most sense to use one or the other to keep your code consistent. For this example, since the configuration is now coded fluently, we've removed all of the Data Annotations. In fact the Model project no longer needs the references to the System.Component Model.DataAnnotations or EntityFramework assemblies.

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


When running the example again, Code First will compare the model to the EdmMeta data table in the database and see that although we changed how we configured the model, the end result is the same. Therefore it will not need to drop and create the database. The same destination will get added to the database again, so you'll end up with matching records. You can see the duplicate data in Figure 2-4.

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


Code First :使用Entity. Framework编程(2)_第4张图片

Organizing Fluent Configurations

组织Fluent配置


If you have a lot of configuration to perform, the OnModelCreating method might quickly become overwhelmed with code. You can group configuration by entity type within individual EntityTypeConfiguration classes, and then tell the DbModelBuilder about
them in the OnModelCreating method. DbModelBuilder has a Configurations property to which you can add these EntityTypeConfigurations.

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


Example 2-11 shows all of the configurations for the Destination class grouped into the DestinationConfiguration class and the same for the Lodging configurations.

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


Example 2-11. Organizing configs into separate EntityTypeConfiguration classes

using System.Data.Entity.ModelConfiguration;

using Model;

public class DestinationConfiguration :

EntityTypeConfiguration

{

public DestinationConfiguration()

{

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

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

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

}

}

public class LodgingConfiguration :

EntityTypeConfiguration

{

public LodgingConfiguration()

{

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

}

}

When these were inside the OnModelCreating method, they began with the DbModelBuilder, followed by the Entity method to identify which entity was being configured. But in an EntityConfiguration class, that is already known, based on the fact that the class is inheriting from EntityTypeConfiguration and the entity type is specified. Therefore, rather than, for example, modelBuilder.Entity().Property, you begin with Property. Calling modelBuilder.Entity() will actually create an 
EntityTypeConfiguration and return it to you, so whichever approach you chose, you are accessing the same API.

当这些代码在OnModelCrating方面内的时候,开始于DbModelBulider,随后用实体方法来确定哪个实体被配置.EntityConfiguration类中,根据继承自EntityTypeConfiguration和指定的实体类型,都是已知的.因此,

modelBuilder.Entity()实际上也将创建一个
EntityTypeConfiguration
并返还给您,所以无论您选择哪种方法,您正在访问相同的API.

And now in Example 2-12, you can see the revised OnModelCreating method that consumes these classes.
瑞在例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());

}

As we move forward with fluent configuration examples in this book,we'll sometimes show the configuration as it would look inside an EntityTypeConfiguration class and other times show the configuration as a modelBuilder configuration. This is just for the sake of letting you continue to see both syntaxes. However, in your production applications,it is more reasonable not to mix up the placement of your configuration.You will most likely want to have a consistent pattern, whether that
means putting all of the configuration inside the OnModelCreating method or always organizing them into EntityTypeConfiguration classes. You'll see that there are a few configuration operations that are not type specific and must go directly in the OnModelCreating method.

随着我们转向Fluent配置的例子,我们有时会显示配置看起来是在EntityTypeConfiguration类中进行的,有时显示配置在modelBulider中进行.这仅仅是为了让你继续看到这两种语法。然而,在生产应用中,很显然不能混用配置,很可能会希望有一个统一的模式,无论是将所有配置都写在OnModelCrating方法内部还是组织在EntityTypeConfiguration类中。你会看到有一些配置操作不是类型具体的,必须要直接在OnModelCreating方法中执行.

Summary

小结


In this chapter you have seen the basics of working with Code First. You've learned that Code First's conventional behavior is quite intelligent, with the ability to guess what your intention is based on what it discovers in your classes. When the convention is not able to infer correctly, you can explicitly control how Code First builds a model and database schema by applying configuration. You learned about configuring directly in the class by applying attributes called Data Annotations. For those who prefer to leave their domain classes alone, you learned how to use the alternative Fluent API to perform configuration in the DbContext class.

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


Code First can automatically build a database for you. You've seen how to exert some control over its response to changes you make in the model with respect to recreating the database to match the new model. Now that you've got the flavor of how Code First works, we'll dig further into all of these topics as we move through the rest of the chapters, expanding the model and learning the ins and outs of modeling with Code First

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


你可能感兴趣的:(SqlServer,DONET#MVC)