实体框架 (EF) 入门 => 3、CodeFirst 支持的完整特性列表

 


KeyAttribute 数据库

设置主键。若是为int类型,将自动设置为自增加列。编程

系统默认以Id或类名+Id做为主键。
StringLengthAttribute 数组

可设置最大最小长度以及验证提示信息等。最大长度会映射到数据库。
MaxLengthAttribute 服务器

最大长度。会映射的数据库。
ConcurrencyCheckAttribute 架构

修改或删除时,将带此属性的列的原有值与主键一块儿传送到数据库,若是传递的值与数据库中不一致,则修改或删除失败。用于并发检查。
RequiredAttribute 并发

必填字段。将映射到数据库,使字段属性为  not null。客户段做必填项验证。
TimestampAttribute app

用于并发检查,一个实体类中只能有一个标记为此属性。框架

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

这样,Code First 将在数据库表中建立一个不可为空的 Timestamp 列。less


ComplexTypeAttribute 函数


ColumnAttribute

指定列名     

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

TableAttribute

指定表名

[Table("InternalBlogs")]
public class Blog

InversePropertyAttribute

两个类之间存在多个关系时,将使用 InverseProperty。
ForeignKeyAttribute

用于外键名称与主键不一致的状况。
DatabaseGeneratedAttribute

Identity 自增加列,通常主键会自动设置此属性,非主键才须要设置。

None 用于不将主键设置为自增加列。

Computed 不让实体框架尝试更新这些列,通常用于使用默认值。

[DatabaseGenerated(DatabaseGenerationOption.Computed)]

不能添加[Required],验证通不过。也没必要添加[Required],数据库中自动设置为 not null。

bool   =>   0   (false)

int     =>   0

float  =>   0

decimal  =>  0

datetime  =>  1900-01-01 00:00:00

byte  =>   0    在数据库中为  tinyint              (取值范围0-255)

short=>   0    在数据库中为  smallyint  

若是设置了     [DatabaseGenerated(DatabaseGenerationOption.Computed)] ,再取消,数据库中也不会修改

除了string 外,基本都容许有默认值

数值类型也没有必要在数据库中添加默认值,值类型在初始化时,自动会赋予默认值并保存到数据库中。

 

datetime 或 string 若是想使用默认值,能够在构造函数中进行初始化。    


NotMappedAttribute

不映射到数据库,通常用于将映射到数据库的字段经过计算获取只读值。

 

经过实体框架 Code First,可使用您本身的域类表示 EF 执行查询、更改跟踪和更新函数所依赖的模型。Code First 利用称为“约定先于配置”的编程模式。这就是说,Code First 将假定您的类听从 EF 所使用的约定。在这种状况下,EF 将可以找出本身工做所需的详细信息。可是,若是您的类不遵照这些约定,则能够向类中添加配置,以向 EF 提供它须要的信息。

Code First 为您提供了两种方法来向类中添加这些配置。一种方法是使用名为 DataAnnotations 的简单特性,另外一种方法是使用 Code First 的 Fluent API,该 API 向您提供了在代码中以命令方式描述配置的方法。

本文重点介绍如何使用 DataAnnotations(在 System.ComponentModel.DataAnnotations 命名空间中)对类进行配置,着重讲述经常使用的配置。不少 .NET 应用程序(如 ASP.NET MVC)都可以理解 DataAnnotations,它容许这些应用程序对客户端验证使用相同的注释。

我将经过 Blog 和 Post 这两个简单的类来讲明 Code First DataAnnotations。

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

Blog 和 Post 类自己就遵照 Code First 约定,无需调整便可让 EF 与之共同使用。但您也可使用注释向 EF 提供有关类以及类所映射到的数据库的更多信息。

实体框架依赖于每一个具备键值的实体,它使用键值来跟踪实体。Code First 依赖的一个约定是它在每个 Code First 类中以何种方式表示哪个属性是键。该约定是查找名为“Id”或类名与“Id”组合在一块儿(如“BlogId”)的属性。该属性将映射到数据库中的主键列。

Blog 和 Post 类都遵照此约定。但若是它们不遵照呢?若是 Blog 使用名称 PrimaryTrackingKey,甚至使用 foo 呢?若是 Code First 找不到符合此约定的属性,它将引起异常,由于实体框架要求必需要有一个键属性。您可使用键注释来指定要将哪个属性用做 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; }
}

若是您在使用 Code First 的数据库生成功能,则 Blog 表将具备名为 PrimaryTrackingKey 的主键列,该列默认状况下还定义为 Identity

必需

Required 注释告诉 EF 某一个特定属性是必需的。

在 Title 属性中添加 Required 将强制 EF(和 MVC)确保该属性中包含数据。

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

MVC 应用程序无需添加其余代码或更改标记,就能执行客户端验证,甚至还能使用属性和注释名称动态生成消息。


Required 特性还将使被映射的属性不可为空来影响生成的数据库。请注意,Title 字段已经更改成“not null”。

MaxLength 和 MinLength

使用 MaxLength 和 MinLength 特性,您能够就像对 Required 那样指定其余属性验证。

下面是具备长度要求的 BloggerName。该示例也说明如何组合特性。

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

MaxLength 注释将经过把属性长度设置为 10 来影响数据库。

MVC 客户端注释和 EF 4.1 服务器端注释都要执行此验证,也会动态生成错误消息:“字段 BloggerName 必须是最大长度为 10 的字符串或数组类型。”该消息有一点长。不少注释都容许您使用 ErrorMessage 特性来指定错误消息。

[MaxLength(10, ErrorMessage="BloggerName 必须在 10 个字符如下"),MinLength(5)] 
public string BloggerName { get; set; }

您也能够在 Required 注释中指定 ErrorMessage。

NotMapped

Code First 约定指示具备受支持数据类型的每一个属性都要在数据库中有表示。但在您的应用程序中并不老是如此。例如,您能够在 Blog 类中使用一个属性来基于 Title 和 BloggerName 字段建立代码。该属性能够动态建立,无需存储。您可使用 NotMapped 注释来标记不映射到数据库的全部属性,以下面的 BlogCode 属性。

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

ComplexType

跨一组类描述域实体,而后将这些类分层以描述一个完整实体的状况并很多见。例如,您能够向模型中添加一个名为 BlogDetails 的类。

public class BlogDetails 
{
    public DateTime?DateCreated { get; set; }
 
    [MaxLength(250)]
    public string Description { get; set; }
}

请注意,BlogDetails 没有任何键属性类型。在域驱动的设计中,BlogDetails 称为值对象。实体框架将值对象称为复杂类型。复杂类型不能自行跟踪。

可是 BlogDetails 做为 Blog 类中的一个属性,将做为 Blog 对象的一部分被跟踪。为了让 Code First 认识到这一点,您必须将 BlogDetails 类标记为 ComplexType。

[ComplexType] 
public class BlogDetails
{
    public DateTime?DateCreated { get; set; }
 
    [MaxLength(250)]
    public string Description { get; set; }
}

如今,您能够在 Blog 类中添加一个属性来表示该博客的 BlogDetails。

    public BlogDetails BlogDetail { get; set; }

在数据库中,Blog 表将包含该博客的全部属性,包括在其 BlogDetail 属性中所含的属性。默认状况下,每一个属性都将添加复杂类型名称前缀 BlogDetail。

另外,有趣的是,虽然 DateCreated 属性在类中定义为不可为空的 DateTime,但相关数据库字段是可为空的。若是想影响数据库架构,则必须使用 Required 注释。

ConcurrencyCheck

ConcurrencyCheck 注释可用于标记要在用户编辑或删除实体时用于在数据库中进行并发检查的一个或多个属性。若是以前使用 EF 设计器,则这等同于将属性的 ConcurrencyMode 设置为 Fixed。

如今让咱们将 ConcurrencyCheck 添加到 BloggerName 属性,看看它如何工做。

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

调用 SaveChanges 时,由于 BloggerName 字段上具备 ConcurrencyCheck 注释,因此在更新中将使用该属性的初始值。该命令将尝试经过同时依据键值和 BloggerName 的初始值进行筛选来查找正确的行。下面是发送到数据库的 UPDATE 命令的关键部分,在其中您能够看到该命令将更新 PrimaryTrackingKey 为 1 且 BloggerName 为“Julie”(这是从数据库中检索到该博客时的初始值)的行。

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

若是在此期间有人更改了该博客的博主姓名,则此更新将失败,并引起 DbUpdateConcurrencyException 而且须要处理该异常。

TimeStamp

使用 rowversion 或 timestamp 字段来进行并发检查更为常见。可是比起使用 ConcurrencyCheck 注释,只要属性类型为字节数组,则不如使用更为具体的 TimeStamp 注释。Code First 将 Timestamp 属性与 ConcurrencyCheck 属性同等对待,但它还将确保 Code First 生成的数据库字段是不可为空的。在一个指定类中,只能有一个 timestamp 属性。

将如下属性添加到 Blog 类:

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

这样,Code First 将在数据库表中建立一个不可为空的 Timestamp 列。

表和列

若是您让 Code First 建立数据库,则可能但愿更改它建立的表和列的名称。也能够将 Code First 用于现有数据库。可是域中的类和属性的名称并不老是与数据库中表和列的名称相匹配。

个人类名为 Blog,按照约定,Code First 将假定此类映射到名为 Blogs 的表。若是不是这样,您能够用 Table 特性指定该表的名称。举例来讲,下面的注释指定表名称为 InternalBlogs。

[Table("InternalBlogs")] 
public class Blog

Column 注释更适于用来指定被映射列的特性。您能够规定名称、数据类型甚至列出如今表中的顺序。下面是 Column 特性的示例。

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

不要将列的 TypeName 特性与 DataType DataAnnotation 相混淆。DataType 是一个用于 UI 的注释,Code First 会将它忽略。

下面是从新生成后的表。表名称已更改成 InternalBlogs,复杂类型的 Description 列如今是 BlogDescription。由于该名称在注释中指定,Code First 不会使用以复杂类型名称做为列名开头的约定。

DatabaseGenerated

一个重要的数据库功能是可使用计算属性。若是您将 Code First 类映射到包含计算列的表,则您可能不想让实体框架尝试更新这些列。可是在插入或更新数据后,您的确须要 EF 从数据库中返回这些值。您可使用 DatabaseGenerated 注释与 Computed 枚举一块儿在您的类中标注这些属性。其余枚举为 None 和 Identity。

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

当 Code First 生成数据库时,您能够对 byte 或 timestamp 列使用生成的数据库,不然您只应该在指向现有数据库时使用,由于 Code First 将不能肯定计算列的公式。

您阅读过以上内容,知道默认状况下,整数键属性将成为数据库中的标识键。这与将 DatabaseGenerated 设置为 DatabaseGenerationOption.Identity 是同样的。若是不但愿它成为标识键,则能够将该值设置为 DatabaseGenerationOption.None。

 

[DatabaseGenerated(DatabaseGenerationOption.Computed)]

不能添加[Required],验证通不过。也没必要添加[Required],数据库中自动设置为 not null。

bool   =>   0   (false)

int     =>   0

float  =>   0

decimal  =>  0

datetime  =>  1900-01-01 00:00:00

byte  =>   0    在数据库中为  tinyint              (取值范围0-255)

short=>   0    在数据库中为  smallyint  

若是设置了     [DatabaseGenerated(DatabaseGenerationOption.Computed)] ,再取消,数据库中也不会修改

除了string 外,基本都容许有默认值    

 

关系特性:InverseProperty 和 ForeignKey

注意:此页面提供有关使用数据注释在 Code First 模型中设置关系的信息。有关 EF 中的关系的通常信息和如何使用关系来访问和操做数据,请参阅 关系和导航属性

Code First 约定将在您的模型中处理最经常使用的关系,可是在某些状况下它须要帮助。

在 Blog 类中更改键属性的名称形成它与 Post 的关系出现问题。 

生成数据库时,Code First 会在 Post 类中看到 BlogId 属性并识别出该属性,按照约定,它与类名加“Id”匹配,并做为 Blog 类的外键。可是在此 Blog 类中没有 BlogId 属性。解决方法是,在 Post 中建立一个导航属性,并使用 Foreign DataAnnotation 来帮助 Code First 了解如何在两个类之间建立关系(那就是使用 Post.BlogId 属性)以及如何在数据库中指定约束。

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.BlogId 之间的关系。 

类之间存在多个关系时,将使用 InverseProperty。

在 Post 类中,您可能须要跟踪是谁撰写了博客文章以及谁编辑了它。下面是 Post 类的两个新的导航属性。

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

您还须要在这些属性引用的 Person 类中添加内容。Person 类具备返回到 Post 的导航属性,一个属性指向该用户撰写的全部文章,一个属性指向该用户更新的全部文章。

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 不能自行使这两个类中的属性匹配。Posts 的数据库表应该有一个表示 CreatedBy 人员的外键,有一个表示 UpdatedBy 人员的外键,可是 Code First 将建立四个外键属性:Person_Id、Person_Id一、CreatedBy_Id 和 UpdatedBy_Id。

要解决这些问题,您可使用 InverseProperty 注释来指定这些属性的匹配。

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

由于 Person 中的 PostsWritten 属性知道这指的是 Post 类型,因此它将与 Post.CreatedBy 创建关系。一样,PostsUpdated 也将与 Post.UpdatedBy 创建关系。Code First 不会建立额外的外键。

 

表内主外键

image

image

相关文章
相关标签/搜索