Code First 数据批注Code First Data Annotations

备注

仅限 ef 4.1 -在实体框架4.1 中引入了本页中所述的功能、api 等。EF4.1 Onwards Only - The features, APIs, etc. discussed in this page were introduced in Entity Framework 4.1. 如果你使用的是早期版本,则不会应用此信息中的部分或全部。If you are using an earlier version, some or all of this information does not apply.

此页面上的内容适用于最初由 Julie Lerman () 编写的文章 <http://thedatafarm.com> 。The content on this page is adapted from an article originally written by Julie Lerman (<http://thedatafarm.com>).

利用实体框架 Code First,你可以使用自己的域类来表示 EF 依赖来执行查询、更改跟踪和更新功能的模型。Entity Framework Code First allows you to use your own domain classes to represent the model that EF relies on to perform querying, change tracking, and updating functions. Code First 利用称为 "约定 over 配置" 的编程模式。Code First leverages a programming pattern referred to as 'convention over configuration.' Code First 将假设你的类遵循实体框架的约定,在这种情况下,将自动处理如何执行其作业。Code First will assume that your classes follow the conventions of Entity Framework, and in that case, will automatically work out how to perform its job. 但是,如果你的类不遵循这些约定,则可以将配置添加到你的类,以便为 EF 提供必需的信息。However, if your classes do not follow those conventions, you have the ability to add configurations to your classes to provide EF with the requisite information.

Code First 提供了向你的类添加这些配置的两种方法。Code First gives you two ways to add these configurations to your classes. 其中一种方法是使用名为 DataAnnotations 的简单特性,第二种方法是使用 Code First 的流畅 API,这为你提供了一种在代码中以强制方式描述配置的方式。One is using simple attributes called DataAnnotations, and the second is using Code First’s Fluent API, which provides you with a way to describe configurations imperatively, in code.

本文重点介绍如何使用 DataAnnotations 命名空间) 中的 DataAnnotations (来配置类-突出显示最常用的配置。This article will focus on using DataAnnotations (in the System.ComponentModel.DataAnnotations namespace) to configure your classes – highlighting the most commonly needed configurations. DataAnnotations 也可由许多 .NET 应用程序(如 ASP.NET MVC)理解,这允许这些应用程序利用相同的注释进行客户端验证。DataAnnotations are also understood by a number of .NET applications, such as ASP.NET MVC which allows these applications to leverage the same annotations for client-side validations.

模型The model

我将使用简单的类对 Code First DataAnnotations 进行演示:博客和文章。I’ll demonstrate Code First DataAnnotations with a simple pair of classes: Blog and Post.

    public class Blog
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string BloggerName { get; set;}
        public virtual ICollection<Post> Posts { get; set; }
    }

    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public DateTime DateCreated { get; set; }
        public string Content { get; set; }
        public int BlogId { get; set; }
        public ICollection<Comment> Comments { get; set; }
    }

正如他们一样,博客和文章类可以方便地遵循 code first 约定,无需进行调整即可启用 EF 兼容性。As they are, the Blog and Post classes conveniently follow code first convention and require no tweaks to enable EF compatability. 但是,您还可以使用批注向 EF 提供有关它们所映射到的类和数据库的详细信息。However, you can also use the annotations to provide more information to EF about the classes and the database to which they map.

 

KeyKey

实体框架依赖于每个实体,每个实体都有一个用于实体跟踪的键值。Entity Framework relies on every entity having a key value that is used for entity tracking. Code First 的一种约定是隐式键属性;Code First 将查找名为 "Id" 的属性,或者查找类名称和 "Id" (如 "BlogId")的组合。One convention of Code First is implicit key properties; Code First will look for a property named “Id”, or a combination of class name and “Id”, such as “BlogId”. 此属性将映射到数据库中的主键列。This property will map to a primary key column in the database.

博客和帖子类都遵循此约定。The Blog and Post classes both follow this convention. 如果不是,怎么办?What if they didn’t? 如果博客使用的是名称 PrimaryTrackingKey (甚至是 foo),该怎么办?What if Blog used the name PrimaryTrackingKey instead, or even foo? 如果代码优先找不到与此约定相匹配的属性,则会引发异常,因为实体框架要求必须有一个键属性。If code first does not find a property that matches this convention it will throw an exception because of Entity Framework’s requirement that you must have a key property. 你可以使用密钥批注来指定要用作 EntityKey 的属性。You can use the key annotation to specify which property is to be used as the EntityKey.

    public class Blog
    {
        [Key]
        public int PrimaryTrackingKey { get; set; }
        public string Title { get; set; }
        public string BloggerName { get; set;}
        public virtual ICollection<Post> Posts { get; set; }
    }

如果使用代码优先的数据库生成功能,博客表将具有名为 PrimaryTrackingKey 的主键列,默认情况下,此列也定义为标识。If you are using code first’s database generation feature, the Blog table will have a primary key column named PrimaryTrackingKey, which is also defined as Identity by default.

带主键的博客表

组合键Composite keys

实体框架支持组合键-由多个属性组成的主键。Entity Framework supports composite keys - primary keys that consist of more than one property. 例如,你可以有一个 Passport 类,其 primary key 是 PassportNumber 和 IssuingCountry 的组合。For example, you could have a Passport class whose primary key is a combination of PassportNumber and IssuingCountry.

    public class Passport
    {
        [Key]
        public int PassportNumber { get; set; }
        [Key]
        public string IssuingCountry { get; set; }
        public DateTime Issued { get; set; }
        public DateTime Expires { get; set; }
    }

尝试在 EF 模型中使用以上类将导致 InvalidOperationExceptionAttempting to use the above class in your EF model would result in an InvalidOperationException:

无法确定类型 "Passport" 的组合键。使用 ColumnAttribute 或 HasKey 方法为组合主键指定顺序。Unable to determine composite primary key ordering for type 'Passport'. Use the ColumnAttribute or the HasKey method to specify an order for composite primary keys.

若要使用组合键,实体框架要求您定义键属性的顺序。In order to use composite keys, Entity Framework requires you to define an order for the key properties. 为此,可以使用列批注指定顺序。You can do this by using the Column annotation to specify an order.

备注

顺序值是相对 (,而不是基于索引) 因此可以使用任何值。The order value is relative (rather than index based) so any values can be used. 例如,100和200可接受而不是1和2。For example, 100 and 200 would be acceptable in place of 1 and 2.

    public class Passport
    {
        [Key]
        [Column(Order=1)]
        public int PassportNumber { get; set; }
        [Key]
        [Column(Order = 2)]
        public string IssuingCountry { get; set; }
        public DateTime Issued { get; set; }
        public DateTime Expires { get; set; }
    }

如果有包含复合外键的实体,则必须指定用于相应主键属性的相同列排序。If you have entities with composite foreign keys, then you must specify the same column ordering that you used for the corresponding primary key properties.

只有外键属性中的相对顺序需要相同,分配给 Order 的确切值不需要匹配。Only the relative ordering within the foreign key properties needs to be the same, the exact values assigned to Order do not need to match. 例如,在下面的类中,可以使用3和4代替1和2。For example, in the following class, 3 and 4 could be used in place of 1 and 2.

    public class PassportStamp
    {
        [Key]
        public int StampId { get; set; }
        public DateTime Stamped { get; set; }
        public string StampingCountry { get; set; }

        [ForeignKey("Passport")]
        [Column(Order = 1)]
        public int PassportNumber { get; set; }

        [ForeignKey("Passport")]
        [Column(Order = 2)]
        public string IssuingCountry { get; set; }

        public Passport Passport { get; set; }
    }

必需Required

Required批注告知 EF 需要特定属性。The Required annotation tells EF that a particular property is required.

向 Title 属性添加所需的将强制 EF (和 MVC) ,以确保属性中包含数据。Adding Required to the Title property will force EF (and MVC) to ensure that the property has data in it.

    [Required]
    public string Title { get; set; }

如果应用程序中没有额外的代码或标记更改,MVC 应用程序将执行客户端验证,甚至使用属性和批注名称动态生成消息。With no additional code or markup changes in the application, an MVC application will perform client side validation, even dynamically building a message using the property and annotation names.

需要标题的 "创建页面" 错误

必需的属性还会影响生成的数据库,方法是将映射的属性设为不可为 null。The Required attribute will also affect the generated database by making the mapped property non-nullable. 请注意,Title 字段已更改为 "not null"。Notice that the Title field has changed to “not null”.

备注

在某些情况下,即使属性是必需的,也无法使数据库中的列不可为 null。In some cases it may not be possible for the column in the database to be non-nullable even though the property is required. 例如,对多个类型使用 TPH 继承战略数据时,会将其存储在一个表中。For example, when using a TPH inheritance strategy data for multiple types is stored in a single table. 如果派生的类型包含所需的属性,则列不能为 null,因为并不是层次结构中的所有类型都具有此属性。If a derived type includes a required property the column cannot be made non-nullable since not all types in the hierarchy will have this property.

 

博客表

 

MaxLength 和 MinLengthMaxLength and MinLength

MaxLengthMinLength 特性使你可以指定其他属性验证,就像对执行的操作一样 RequiredThe MaxLength and MinLength attributes allow you to specify additional property validations, just as you did with Required.

下面是具有长度要求的 BloggerName。Here is the BloggerName with length requirements. 该示例还演示了如何合并特性。The example also demonstrates how to combine attributes.

    [MaxLength(10),MinLength(5)]
    public string BloggerName { get; set; }

MaxLength 批注将属性的长度设置为10,将影响数据库。The MaxLength annotation will impact the database by setting the property’s length to 10.

显示 BloggerName 列的最大长度的博客表格

MVC 客户端批注和 EF 4.1 服务器端批注都将接受此验证,并再次动态生成错误消息: "字段 BloggerName 必须是最大长度为" 10 "的字符串或数组类型。"该消息只需很长时间。MVC client-side annotation and EF 4.1 server-side annotation will both honor this validation, again dynamically building an error message: “The field BloggerName must be a string or array type with a maximum length of '10'.” That message is a little long. 许多批注允许您使用 ErrorMessage 特性指定错误消息。Many annotations let you specify an error message with the ErrorMessage attribute.

    [MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
    public string BloggerName { get; set; }

您还可以在所需的批注中指定 ErrorMessage。You can also specify ErrorMessage in the Required annotation.

创建具有自定义错误消息的页面

 

NotMappedNotMapped

Code first 约定规定每个属于受支持数据类型的属性都在数据库中表示。Code first convention dictates that every property that is of a supported data type is represented in the database. 但在应用程序中并不总是如此。But this isn’t always the case in your applications. 例如,你可能在博客类中有一个属性,该属性基于 "标题" 和 "BloggerName" 字段创建代码。For example you might have a property in the Blog class that creates a code based on the Title and BloggerName fields. 该属性可以动态创建,不需要存储。That property can be created dynamically and does not need to be stored. 可以用 NotMapped 批注(如此 BlogCode 属性)来标记不映射到数据库的任何属性。You can mark any properties that do not map to the database with the NotMapped annotation such as this BlogCode property.

    [NotMapped]
    public string BlogCode
    {
        get
        {
            return Title.Substring(0, 1) + ":" + BloggerName.Substring(0, 1);
        }
    }

 

ComplexTypeComplexType

请不要在一组类中描述域实体,然后将这些类分层以描述完整的实体。It’s not uncommon to describe your domain entities across a set of classes and then layer those classes to describe a complete entity. 例如,可以将名为 BlogDetails 的类添加到模型。For example, you may add a class called BlogDetails to your model.

    public class BlogDetails
    {
        public DateTime? DateCreated { get; set; }

        [MaxLength(250)]
        public string Description { get; set; }
    }

请注意,没有 BlogDetails 任何类型的键属性。Notice that BlogDetails does not have any type of key property. 在域驱动设计中, BlogDetails 称为值对象。In domain driven design, BlogDetails is referred to as a value object. 实体框架将值对象引用为复杂类型。Entity Framework refers to value objects as complex types.不能自行跟踪复杂类型。  Complex types cannot be tracked on their own.

但作为类中的属性 BlogBlogDetails 将作为对象的一部分进行跟踪 BlogHowever as a property in the Blog class, BlogDetails will be tracked as part of a Blog object. 为了使代码优先识别此项,必须将 BlogDetails 类标记为 ComplexTypeIn order for code first to recognize this, you must mark the BlogDetails class as a ComplexType.

    [ComplexType]
    public class BlogDetails
    {
        public DateTime? DateCreated { get; set; }

        [MaxLength(250)]
        public string Description { get; set; }
    }

现在,你可以在类中添加一个属性 Blog ,用于表示 BlogDetails 该博客的。Now you can add a property in the Blog class to represent the BlogDetails for that blog.

        public BlogDetails BlogDetail { get; set; }

在数据库中, Blog 表将包含博客的所有属性,包括其属性中包含的属性 BlogDetailIn the database, the Blog table will contain all of the properties of the blog including the properties contained in its BlogDetail property. 默认情况下,每个名称前面都带有复杂类型 "BlogDetail" 的名称。By default, each one is preceded with the name of the complex type, "BlogDetail".

包含复杂类型的博客表

ConcurrencyCheckConcurrencyCheck

ConcurrencyCheck使用批注,可以标记一个或多个属性,以便在用户编辑或删除实体时,将该属性用于数据库中的并发检查。The ConcurrencyCheck annotation allows you to flag one or more properties to be used for concurrency checking in the database when a user edits or deletes an entity. 如果你使用的是 EF 设计器,则这与将属性设置为对齐 ConcurrencyMode FixedIf you've been working with the EF Designer, this aligns with setting a property's ConcurrencyMode to Fixed.

让我们 ConcurrencyCheck 通过将其添加到属性来了解其工作原理 BloggerNameLet’s see how ConcurrencyCheck works by adding it to the BloggerName property.

    [ConcurrencyCheck, MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
    public string BloggerName { get; set; }

SaveChanges 调用时,由于 ConcurrencyCheck 字段上的批注 BloggerName ,将在更新中使用该属性的原始值。When SaveChanges is called, because of the ConcurrencyCheck annotation on the BloggerName field, the original value of that property will be used in the update. 命令不仅通过筛选键值,还会尝试查找正确的行,而是对的原始值进行筛选 BloggerNameThe command will attempt to locate the correct row by filtering not only on the key value but also on the original value of BloggerName.下面是发送到数据库的 UPDATE 命令的关键部分,您可以在其中看到命令将更新包含1的行 PrimaryTrackingKey 和 "Julie" 的行, BloggerName 这是从数据库中检索到该博客时的原始值。  Here are the critical parts of the UPDATE command sent to the database, where you can see the command will update the row that has a PrimaryTrackingKey is 1 and a BloggerName of “Julie” which was the original value when that blog was retrieved from the database.

    where (([PrimaryTrackingKey] = @4) and ([BloggerName] = @5))
    @4=1,@5=N'Julie'

如果用户同时更改了博客的博客名称,则此更新将会失败,并且你将收到需要处理的 DbUpdateConcurrencyExceptionIf someone has changed the blogger name for that blog in the meantime, this update will fail and you’ll get a DbUpdateConcurrencyException that you'll need to handle.

 

时间戳TimeStamp

更常见的情况是使用 rowversion 或时间戳字段进行并发检查。It's more common to use rowversion or timestamp fields for concurrency checking. 但不使用 ConcurrencyCheck 批注, TimeStamp 只要属性的类型是字节数组,就可以使用更具体的批注。But rather than using the ConcurrencyCheck annotation, you can use the more specific TimeStamp annotation as long as the type of the property is byte array. 代码优先会将 Timestamp 属性视为与 ConcurrencyCheck 属性相同,但它还将确保代码第一个生成的数据库字段不可为 null。Code first will treat Timestamp properties the same as ConcurrencyCheck properties, but it will also ensure that the database field that code first generates is non-nullable. 给定的类中只能有一个时间戳属性。You can only have one timestamp property in a given class.

将以下属性添加到博客类:Adding the following property to the Blog class:

    [Timestamp]
    public Byte[] TimeStamp { get; set; }

在数据库表中生成一个不可以为 null 的时间戳列的代码。results in code first creating a non-nullable timestamp column in the database table.

带有时间戳列的博客表

 

表和列Table and Column

如果允许 Code First 创建数据库,则可能需要更改要创建的表和列的名称。If you are letting Code First create the database, you may want to change the name of the tables and columns it is creating. 您还可以将 Code First 与现有数据库一起使用。You can also use Code First with an existing database. 但并非总是域中类和属性的名称与数据库中的表和列的名称相匹配。But it's not always the case that the names of the classes and properties in your domain match the names of the tables and columns in your database.

Blog 的类按约定命名,代码优先假设这将映射到名为的表 BlogsMy class is named Blog and by convention, code first presumes this will map to a table named Blogs. 如果不是这种情况,则可以指定具有属性的表的名称 TableIf that's not the case you can specify the name of the table with the Table attribute. 例如,批注正在将表名指定为 InternalBlogsHere for example, the annotation is specifying that the table name is InternalBlogs.

    [Table("InternalBlogs")]
    public class Blog

Column在指定映射列的属性的同时,批注更加熟练。The Column annotation is a more adept in specifying the attributes of a mapped column. 您可以规定名称、数据类型,甚至是列在表中的显示顺序。You can stipulate a name, data type or even the order in which a column appears in the table. 下面是属性的示例 ColumnHere is an example of the Column attribute.

    [Column("BlogDescription", TypeName="ntext")]
    public String Description {get;set;}

不要将列的 TypeName 属性与 DataType DataAnnotation 混淆。Don’t confuse Column’s TypeName attribute with the DataType DataAnnotation. 数据类型是一种用于 UI 的批注,Code First 会将其忽略。DataType is an annotation used for the UI and is ignored by Code First.

下面是重新生成表后的表。Here is the table after it’s been regenerated. 表名称已更改为 InternalBlogs ,并且 Description 复杂类型中的列现在是 BlogDescriptionThe table name has changed to InternalBlogs and Description column from the complex type is now BlogDescription. 由于在注释中指定了名称,因此 code first 将不会使用以复杂类型名称开头的列名称。Because the name was specified in the annotation, code first will not use the convention of starting the column name with the name of the complex type.

博客表和列已重命名

 

DatabaseGeneratedDatabaseGenerated

重要的数据库功能是具有计算属性的能力。An important database features is the ability to have computed properties. 如果要将 Code First 类映射到包含计算列的表,则不希望实体框架尝试更新这些列。If you're mapping your Code First classes to tables that contain computed columns, you don't want Entity Framework to try to update those columns. 但是,在插入或更新数据后,您确实需要 EF 从数据库返回这些值。But you do want EF to return those values from the database after you've inserted or updated data. 你可以使用 DatabaseGenerated 批注来标记类中的这些属性以及 Computed 枚举。You can use the DatabaseGenerated annotation to flag those properties in your class along with the Computed enum. 其他枚举为 NoneIdentityOther enums are None and Identity.

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime DateCreated { get; set; }

当代码优先生成数据库时,可以使用在字节或时间戳列上生成的数据库,否则,只应在指向现有数据库时使用此数据库,因为 code first 无法确定计算列的公式。You can use database generated on byte or timestamp columns when code first is generating the database, otherwise you should only use this when pointing to existing databases because code first won't be able to determine the formula for the computed column.

如上所述,在默认情况下,作为整数的键属性将成为数据库中的标识键。You read above that by default, a key property that is an integer will become an identity key in the database. 这与将设置 DatabaseGenerated 为相同 DatabaseGeneratedOption.IdentityThat would be the same as setting DatabaseGenerated to DatabaseGeneratedOption.Identity. 如果你不希望它是标识密钥,则可以将值设置为 DatabaseGeneratedOption.NoneIf you do not want it to be an identity key, you can set the value to DatabaseGeneratedOption.None.

 

索引Index

备注

Ef 6.1 仅 往上- Index 实体框架6.1 中引入了属性。EF6.1 Onwards Only - The Index attribute was introduced in Entity Framework 6.1. 如果你使用的是早期版本,则本部分中的信息不适用。If you are using an earlier version the information in this section does not apply.

您可以使用 IndexAttribute对一个或多个列创建索引。You can create an index on one or more columns using the IndexAttribute. 将属性添加到一个或多个属性时,将导致 EF 在创建数据库时在数据库中创建相应的索引,或者如果使用 Code First 迁移,则为相应的 CreateIndex 调用基架。Adding the attribute to one or more properties will cause EF to create the corresponding index in the database when it creates the database, or scaffold the corresponding CreateIndex calls if you are using Code First Migrations.

例如,下面的代码将生成在数据库的表的列上创建的索引 Rating PostsFor example, the following code will result in an index being created on the Rating column of the Posts table in the database.

    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        [Index]
        public int Rating { get; set; }
        public int BlogId { get; set; }
    }

默认情况下,上述示例中的索引将命名为ix _ < > 属性名称 (ix _ 分级) 。By default, the index will be named IX_<property name> (IX_Rating in the above example). 您还可以指定索引的名称。You can also specify a name for the index though. 下面的示例指定索引应命名为 PostRatingIndexThe following example specifies that the index should be named PostRatingIndex.

    [Index("PostRatingIndex")]
    public int Rating { get; set; }

默认情况下,索引是不唯一的,但您可以使用 IsUnique 命名参数指定索引应是唯一的。By default, indexes are non-unique, but you can use the IsUnique named parameter to specify that an index should be unique. 下面的示例对 User 的登录名引入唯一索引。The following example introduces a unique index on a User's login name.

    public class User
    {
        public int UserId { get; set; }

        [Index(IsUnique = true)]
        [StringLength(200)]
        public string Username { get; set; }

        public string DisplayName { get; set; }
    }

多列索引Multiple-Column Indexes

跨多个列的索引通过在给定表的多个索引批注中使用相同的名称来指定。Indexes that span multiple columns are specified by using the same name in multiple Index annotations for a given table. 创建多列索引时,需要指定索引中列的顺序。When you create multi-column indexes, you need to specify an order for the columns in the index. 例如,下面的代码在上创建一个多列索引 Rating ,并将其 BlogId 称为 IX _ BlogIdAndRatingFor example, the following code creates a multi-column index on Rating and BlogId called IX_BlogIdAndRating. BlogId 是索引中的第一列, Rating 是第二列。BlogId is the first column in the index and Rating is the second.

    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        [Index("IX_BlogIdAndRating", 2)]
        public int Rating { get; set; }
        [Index("IX_BlogIdAndRating", 1)]
        public int BlogId { get; set; }
    }

 

关系属性: InverseProperty 和 ForeignKeyRelationship Attributes: InverseProperty and ForeignKey

备注

本页提供有关使用数据批注设置 Code First 模型中的关系的信息。This page provides information about setting up relationships in your Code First model using Data Annotations. 有关 EF 中的关系以及如何使用关系访问和操作数据的一般信息,请参阅 关系 & 导航属性。 *For general information about relationships in EF and how to access and manipulate data using relationships, see Relationships & Navigation Properties.*

Code first 约定将负责您的模型中最常见的关系,但在某些情况下,需要帮助。Code first convention will take care of the most common relationships in your model, but there are some cases where it needs help.

更改类中的键属性的名称会 Blog 创建一个与的关系相关的问题 PostChanging the name of the key property in the Blog class created a problem with its relationship to Post. 

生成数据库时,代码优先会在 BlogId Post 类中看到该属性,并将其识别为与类名称和 Id相匹配的约定,作为类的外键 BlogWhen generating the database, code first sees the BlogId property in the Post class and recognizes it, by the convention that it matches a class name plus Id, as a foreign key to the Blog class. BlogId 博客类中没有属性。But there is no BlogId property in the blog class. 此操作的解决方案是在中创建导航属性 Post ,并使用 ForeignKey DataAnnotation 帮助代码首先了解如何使用属性) 在两个 (类之间建立关系,以及 Post.BlogId 如何在数据库中指定约束。The solution for this is to create a navigation property in the Post and use the ForeignKey DataAnnotation to help code first understand how to build the relationship between the two classes (using the Post.BlogId property) as well as how to specify constraints in the database.

    public class Post
    {
            public int Id { get; set; }
            public string Title { get; set; }
            public DateTime DateCreated { get; set; }
            public string Content { get; set; }
            public int BlogId { get; set; }
            [ForeignKey("BlogId")]
            public Blog Blog { get; set; }
            public ICollection<Comment> Comments { get; set; }
    }

数据库中的约束显示与之间的关系 InternalBlogs.PrimaryTrackingKey Posts.BlogIdThe constraint in the database shows a relationship between InternalBlogs.PrimaryTrackingKey and Posts.BlogId. 

InternalBlogs 和 PrimaryTrackingKey 之间的关系。 BlogId

InverseProperty当类之间存在多个关系时,将使用。The InverseProperty is used when you have multiple relationships between classes.

Post 类中,你可能需要跟踪博客文章的作者,以及编辑者。In the Post class, you may want to keep track of who wrote a blog post as well as who edited it. 下面是 Post 类的两个新导航属性。Here are two new navigation properties for the Post class.

    public Person CreatedBy { get; set; }
    public Person UpdatedBy { get; set; }

还需要添加到 Person 这些属性引用的类中。You’ll also need to add in the Person class referenced by these properties. Person类具有返回到的导航属性 Post ,一个用于用户编写的所有张贴内容,一个用于由该用户更新的所有发布。The Person class has navigation properties back to the Post, one for all of the posts written by the person and one for all of the posts updated by that person.

    public class Person
    {
            public int Id { get; set; }
            public string Name { get; set; }
            public List<Post> PostsWritten { get; set; }
            public List<Post> PostsUpdated { get; set; }
    }

Code first 不能自行匹配两个类中的属性。Code first is not able to match up the properties in the two classes on its own. 的数据库表 Posts 应有一个外键 CreatedBy ,而另一个用于用户,另一个用于 UpdatedBy 用户,但 code first 将创建四个外键属性: person _ idperson _ Id1system.createdby _ idUpdatedBy _ idThe database table for Posts should have one foreign key for the CreatedBy person and one for the UpdatedBy person but code first will create four foreign key properties: Person_Id, Person_Id1, CreatedBy_Id and UpdatedBy_Id.

带有额外外键的 post 表

若要解决这些问题,可以使用 InverseProperty 批注来指定属性的对齐方式。To fix these problems, you can use the InverseProperty annotation to specify the alignment of the properties.

    [InverseProperty("CreatedBy")]
    public List<Post> PostsWritten { get; set; }

    [InverseProperty("UpdatedBy")]
    public List<Post> PostsUpdated { get; set; }

因为 PostsWritten Person 中的属性知道这是指该 Post 类型,所以它将与建立关系 Post.CreatedByBecause the PostsWritten property in Person knows that this refers to the Post type, it will build the relationship to Post.CreatedBy. 同样, PostsUpdated 将连接到 Post.UpdatedBySimilarly, PostsUpdated will be connected to Post.UpdatedBy. 和 code first 不会创建额外的外键。And code first will not create the extra foreign keys.

发送无额外外键的表

 

总结Summary

DataAnnotations 不仅使你能够在代码优先类中描述客户端和服务器端验证,还允许你增强,甚至更正代码优先根据其约定对类进行的假设。DataAnnotations not only let you describe client and server side validation in your code first classes, but they also allow you to enhance and even correct the assumptions that code first will make about your classes based on its conventions. 使用 DataAnnotations,不仅可以驱动数据库架构生成,还可以将代码的第一类映射到预先存在的数据库。With DataAnnotations you can not only drive database schema generation, but you can also map your code first classes to a pre-existing database.

尽管它们非常灵活,但请记住,DataAnnotations 仅提供您可以在代码优先类上进行的最常见的配置更改。While they are very flexible, keep in mind that DataAnnotations provide only the most commonly needed configuration changes you can make on your code first classes. 若要为某些边缘事例配置类,应查看备用配置机制,Code First 的 "流畅" API。To configure your classes for some of the edge cases, you should look to the alternate configuration mechanism, Code First’s Fluent API .