EF Code-First提供了一个可以用在领域类或其属性上的DataAnnotation特性集合,DataAnnotation特性会覆盖默认的EF约定。

DataAnnotation存在于两个命名空间里:

System.ComponentModel.DataAnnotationsSystem.ComponentModel.DataAnnotations.Schema

注意: DataAnnotations只提供了一部分的配置选项,全部的配置选项在Fluent API中。

System.ComponentModel.DataAnnotations 包含的特性:

Attribute 描述
Key 标记一个属性,其将会在关系表中被映射成主键
Timestamp 标记一个属性,其将会在数据库中被映射成一个不为null的tiamestamp(时间戳)列
ConcurrencyCheck 这个属性允许你标记一个或多个属性,被标记的属性将会在用户编辑或删除entity的时候进行并发检查
Required 强制约束,该属性必须有数据,不能为null(同样适用MVC)
MinLength 确保数组或字符串长度达到最小长度
MaxLength 数据库中列的长度的最大值
StringLength 在数据字段中指定字符允许的最大长度和最小长度

System.ComponentModel.DataAnnotations.Schema 包含的特性:

Attribute 描述
Table 指定被映射的类在数据库生成的表名
Column 指定被映射的属性在表中的列名和数据类型
Index 在指定列上创建索引(仅EF6.1以上版本支持)
ForeignKey 给导航属性指定外键属性
NotMapped 标记的属性不会被映射到数据库
DatabaseGenerated 指定的属性将会映射成数据库表中的计算列,所以这个属性应是只读的。也可以用在把属性映射成标识列(自增长列)
InverseProperty 当两个类之间包含多重关系的时候,默认约定会排列组合他们的导航属性组合并一一创建外键,InverseProperty可以标记实际的主外键关系,从而过滤掉因排列组合出来的无用外键
ComplexType 标记一个类为复杂类型

下面我们来详细介绍各个特性:

1、Key:

Key特性应用在类的属性上。Code-First默认约定将名称是"Id"或者{类名}+"Id"的属性创建为一个主键列,Key特性覆写了默认约定,我们可以把任何想要成为的主键的属性标记为Key而不管它是什么名称。

代码如下:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ } [Key]
public int StudentKey { get; set; } public string StudentName { get; set; } }

数据库中,Students表中的StudentKey列被创建成了主键

我们也可以用用Key特性和Column特性创建混合主键,如下代码所示:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ }
[Key]
[Column(Order=)]
public int StudentKey1 { get; set; } [Key]
[Column(Order=)]
public int StudentKey2 { get; set; } public string StudentName { get; set; } }

根据上面的代码,在Students表中,创建出了混合主键StudentKey1和StudentKey2

注意: 当Key特性应用在单整型类型的属性上时,会将其创建为一个标识列,而混合键无论它是不是整型类型,都不会创建标识列。Key特性除了无符号整型(unsinged integers),可以应用在如string、datatime、decimal等任何数据类型上。

2、TimeStamp:

TimeStamp特性只能用在数据类型为byte array的属性上,TimeStamp特性会在数据库表中创建一个timestamp属性的列,Code-First自动使用TimeStamp列进行并发检查。

(关于并发检查,可以参考Gyoung的笔记:Entity Framework 并发处理

代码如下:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ } public int StudentKey { get; set; } public string StudentName { get; set; } [TimeStamp]
public byte[] RowVersion { get; set; }
}

再次强调,标记TimeStamp特性的属性类型必须是byte数组。

这样,在数据库Students表中就把RowVersion列创建成了timestamp(时间戳)

3、ConcurrencyCheck:

当EF对表执行update命令时,Code-First会把标记了ConcurrencyCheck特性的列中的值插入到SQL语句的“where”子句中来进行并发检查。如下代码:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ } public int StudentId { get; set; } [ConcurrencyCheck]
public string StudentName { get; set; }
}

如上所示,StudentName属性上标记了ConcurrencyCheck特性,所以Code-First会在update命令中把StudentName列包含进去以进行乐观并发检查(有关乐观并发和悲观并发,上面Gyoung的笔记有介绍,这里就不多讨论)。如下代码所示:

exec sp_executesql N'UPDATE [dbo].[Students]
SET [StudentName] = @0
WHERE (([StudentId] = @1) AND ([StudentName] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Steve',@1=1,@2=N'Bill'
go

注意:TimeStamp特性只能用在single byte数组属性上,然而ConcurrencyCheck特性可以用在任何数量和任何数据类型的属性上。

4、Required:

应用了Required特性的属性,将会在数据库表中创建一个不为null的列,需要留意的是,Required也是MVC的验证特性。

代码如下:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ }
public int StudentID { get; set; } [Required]
public string StudentName { get; set; } }

在数据库中,StudentName列已经被创建成不为空。

5、MaxLength & MinLength:

Maxlength特性用在String或array类型的属性上,EF Code-First将会把列的大小设置成指定值。值得注意的是,Maxlength也是MVC的验证特性。

代码如下:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ }
public int StudentID { get; set; } [MaxLength()]
public string StudentName { get; set; } }

因为StudentName属性被指定了[Maxlength(50)]特性,所以在数据库中StudentName列被创建成nvarchar(50)。

Entity Framework也会验证被标记了MaxLength特性的属性的值,如果该值大于被标记的最大值,EF将会抛出EntityValidationError。

MinLength:

MinLength特性是EF的一个验证特性,其在数据库模式中不起作用。如果我们对标记了MinLength特性的属性赋值(string或者array),其长度小于指定的最小值,那么EF仍然会抛出EntityValidationError。

MinLength特性可以和MaxLength特性一起使用,如下代码所示:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ }
public int StudentID { get; set; } [MaxLength(),MinLength()]
public string StudentName { get; set; } }

如上代码所示,StudentName属性取值指定了只能是2-50个字符长度之间。

6、StringLength:

StringLength应用在string类型的属性上,EF Code-First将会用StringLength指定的长度设置列的大小。和Required以及Maxlength一样,StringLength也是MVC的验证特性。

看下面的代码:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ }
public int StudentID { get; set; } [StringLength()]
public string StudentName { get; set; } }

根据代码中StudentName属性的[StringLength(50)]特性,在数据库中,将会创建一个nvarchar(50)的列,如下所示:

同理,EF也将会验证StringLength特性中的值,如果用户输入的值大于指定的长度,将会抛出EntityValidationError。

7、Table:

Table特性应用在类上,默认的Code-First约定将会创建一个和类名同名的表名,Table特性覆写默认的约定,EF Code-First将会创建一个以Table特性里指定的字符串为名称的表。

代码如下:

using System.ComponentModel.DataAnnotations.Schema;

[Table("StudentMaster")]
public class Student
{
public Student()
{ }
public int StudentID { get; set; } public string StudentName { get; set; } }

如上所示,Student类上应用了Table["StudentMaster"]特性,所以Code-First会覆写默认的约定,创建一个名称为StudentMaster的表名

我们也可以用Table特性为表指定一个架构名,代码如下所示:

using System.ComponentModel.DataAnnotations.Schema;

[Table("StudentMaster", Schema="Admin")]
public class Student
{
public Student()
{ }
public int StudentID { get; set; } public string StudentName { get; set; } }

数据库如下,Code-First将会在Admin架构下创建一个StudentMaster表

8、Column:

Column特性应用在类的属性上,和Table特性一样,如果不指定Column特性的值,将会默认创建和属性同名的列,否则就会创建指定的值。

看如下代码:

using System.ComponentModel.DataAnnotations.Schema;

public class Student
{
public Student()
{ }
public int StudentID { get; set; } [Column("Name")]
public string StudentName { get; set; } }

如上所示,Column["Name"]特性应用在StudentName属性上,所以Code-First将会创建一个以"Name"为名的列来代替默认的"StudentName"列名。数据库如下:

我们也可以使用Column特性为列指定排序(order)和类型(type),代码如下:

using System.ComponentModel.DataAnnotations.Schema;

public class Student
{
public Student()
{ }
public int StudentID { get; set; } [Column("Name", Order=, TypeName="varchar")]
public string StudentName { get; set; } }

上面的代码在数据库Students表中创建了一个属性为varchar,排序第一的列Name

9、ForeignKey:

ForeignKey特性应用在类的属性上。默认的Code-First约定预料外键属性名与主键属性名匹配,如下代码:

public class Student
{
public Student()
{ }
public int StudentID { get; set; }
public string StudentName { get; set; } //Foreign key for Standard
public int StandardId { get; set; } public Standard Standard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardId { get; set; }
public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }
}

如上代码所示,Student类包含了外键属性StandardId,其又是Standard类的主键属性,这样,Code-First将会在Students表中创建一个StandardId外键列。

ForeignKey特性覆写了默认约定,我们可以把外键属性列设置成不同名称,代码如下:

public class Student
{
public Student()
{ }
public int StudentID { get; set; }
public string StudentName { get; set; } //Foreign key for Standard
public int StandardRefId { get; set; } [ForeignKey("StandardRefId")]
public Standard Standard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardId { get; set; }
public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }

如上代码所示,Student类包含了StandardRefId外键属性,我们使用ForeignKey["StandardRefId"]特性指定在Standard导航属性上,所以Code-First将会把StandardRefId作为外键,生成数据库如下所示:

ForeignKey特性也可以用在外键属性上,只要指定好它的导航属性,即Standard属性,如下所示:

public class Student
{
public Student()
{ }
public int StudentID { get; set; }
public string StudentName { get; set; } //Foreign key for Standard [ForeignKey("Standard")]
public int StandardRefId { get; set; } public Standard Standard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardId { get; set; }
public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }

这段代码和上面把ForeignKey特性定义在Standard属性上的效果是一样的,在数据库生成的Students表都创建了StandardRefId外键列。

10、NotMapped:

NotMapped特性用在类的属性上,默认Code-First约定会为那些所有包含了getter和setter的属性创建列,NotMapped可以覆写默认的约定,让那些标记了NotMapped特性的属性不会在数据库里创建列。代码如下:

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ } public int StudentId { get; set; } public string StudentName { get; set; } [NotMapped]
public int Age { get; set; }
}

如上代码所示,NotMapped特性应用在Age属性上,所以Code-First不会在Students表中创建Age列。

Code-First也不会为那些没有getter和setter的属性创建列,在下面代码例子中,Code-First不会为FirstName和Age创建列。

using System.ComponentModel.DataAnnotations;

public class Student
{
public Student()
{ }
private int _age = ; public int StudentId { get; set; } public string StudentName { get; set; } public string FirstName { get{ return StudentName;} }
public string Age { set{ _age = value;} } }

11、InverseProperty:

我们已经知道,如果类中没有包含外键属性,Code-First默认约定会创建一个{类名}_{主键}的外键列。当我们类与类之间有多个关系的时候,就可以使用InverseProperty特性。

代码如下:

public class Student
{
public Student()
{ }
public int StudentID { get; set; }
public string StudentName { get; set; } public Standard CurrentStandard { get; set; }
public Standard PreviousStandard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardId { get; set; }
public string StandardName { get; set; } public ICollection<Student> CurrentStudents { get; set; }
public ICollection<Student> PreviousStudents { get; set; } }
}

如上代码所示,Student类包含了两个Standard类的导航属性,同样的,Standard类包含了两个Student类的集合导航属性,Code-First将会为这种关系创建4个列。如下所示:

InverseProperty覆写了这种默认约定并且指定对齐属性,下面的代码在Standard类中使用InverseProperty特性修复这个问题。

public class Student
{
public Student()
{ }
public int StudentID { get; set; }
public string StudentName { get; set; } public Standard CurrentStandard { get; set; }
public Standard PreviousStandard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardId { get; set; }
public string StandardName { get; set; } [InverseProperty("CurrentStandard")]
public ICollection<Student> CurrentStudents { get; set; } [InverseProperty("PreviousStandard")]
public ICollection<Student> PreviousStudents { get; set; } }
}

如上代码所示,我们在CurrentStudents和PreviousStudents属性上应用了InverseProperty特性,并且指定哪个Student类的引用属性属于它,所以现在,Code-First在Student表中仅仅会创建两个外键了。如下图所示:

当然,如果你想改外键名称,我们就给导航属性加上ForeignKey特性,如下代码所示:

public class Student
{
public Student()
{ }
public int StudentID { get; set; }
public string StudentName { get; set; } public int CurrentStandardId { get; set; }
public int PreviousStandardId { get; set; } [ForeignKey("CurrentStandardId")]
public Standard CurrentStandard { get; set; } [ForeignKey("PreviousStandardId")]
public Standard PreviousStandard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardId { get; set; }
public string StandardName { get; set; } [InverseProperty("CurrentStandard")]
public ICollection<Student> CurrentStudents { get; set; } [InverseProperty("PreviousStandard")]
public ICollection<Student> PreviousStudents { get; set; } }

上面的代码将会创建出下面的数据库表和列,我们可以看出,外键的名称已经改变了。

到此,DataAnnotation已经介绍完了,如果有什么不明白,或者我没讲清楚的地方,请大家留言~如有错误,也希望大神指出,不甚感激!

下篇开始,我们开始讲Fluent API,嗯,先睡了。。。

EntityFramework Code-First 简易教程(六)-------领域类配置之DataAnnotations的更多相关文章

  1. EntityFramework Code-First—领域类配置之DataAnnotations

    本文出自:https://www.cnblogs.com/tang-tang/p/5510574.html 一.摘要 EF Code-First提供了一个可以用在领域类或其属性上的DataAnnota ...

  2. EntityFramework Code-First 简易教程(七)-------领域类配置之Fluent API

    Fluent API配置: 前面我们已经了解到使用DataAnotations特性来覆写Code-First默认约定,现在我们来学习Fluent API. Fluent API是另一种配置领域类的方法 ...

  3. EntityFramework Code-First 简易教程(五)-------领域类配置

    前言:在前篇中,总是把领域类(Domain Class)翻译成模型类,因为我的理解它就是一个现实对象的抽象模型,不知道对不对.以防止将来可能的歧义,这篇开始还是直接对Domain Class直译. 前 ...

  4. WebGL简易教程(六):第一个三维示例(使用模型视图投影变换)

    目录 1. 概述 2. 示例:绘制多个三角形 2.1. Triangle_MVPMatrix.html 2.2. Triangle_MVPMatrix.js 2.2.1. 数据加入Z值 2.2.2. ...

  5. EntityFramework Code-First-------领域类配置之DataAnnotations

    EF Code-First提供了一个可以用在领域类或其属性上的DataAnnotation特性集合,DataAnnotation特性会覆盖默认的EF约定. DataAnnotation存在于两个命名空 ...

  6. Entity Frame Code First 简易教程

    简介 什么是ORM 搭建Entity FrameWork CodeFirst应用 数据库迁移 表属性常见配置 Entity FrameWork 一对多.多对多 一.简介 Entity Framewor ...

  7. Dart 语言简易教程系列

    google Fuchsia系统 及 dart语言简介 在 InteIIiJ IDEA 中搭建 Dart 的开发环境 Dart Linux 开发环境搭建 Dart 语言简易教程(一) Dart 语言简 ...

  8. WebGL简易教程(七):绘制一个矩形体

    目录 1. 概述 2. 示例 2.1. 顶点索引绘制 2.2. MVP矩阵设置 2.2.1. 模型矩阵 2.2.2. 投影矩阵 2.2.3. 视图矩阵 2.2.4. MVP矩阵 3. 结果 4. 参考 ...

  9. WebGL简易教程——目录

    目录 1. 绪论 2. 目录 3. 资源 1. 绪论 最近研究WebGL,看了<WebGL编程指南>这本书,结合自己的专业知识写的一系列教程.之前在看OpenGL/WebGL的时候总是感觉 ...

随机推荐

  1. Python面试真题答案或案例

    Python面试真题答案或案例如下: 请等待. #coding=utf-8 #1.一行代码实现1--100之和 print(sum(range(1,101))) #2.如何在一个函数内部修改全局变量 ...

  2. shell脚本中单双引号疑惑

    工作中常用到shell脚本,遇到了一个疑惑,求解.    常见的解释: 单引号字符串的限制: 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的: 单引号字串中不能出现单独一个的单引号(对单 ...

  3. PyCharm2019 激活码

    因公司的需求,需要做一个爬取最近上映的电影.列车号.航班号.机场.车站等信息,所以需要我做一个爬虫项目,当然java也可以做爬虫,但是还是没有python这样方便,所以也开始学习Python啦!!! ...

  4. SpringCloud(5)---Feign服务调用

    SpringCloud(5)---Feign服务调用 上一篇写了通过Ribbon进行服务调用,这篇其它都一样,唯一不一样的就是通过Feign进行服务调用. 注册中心和商品微服务不变,和上篇博客一样,具 ...

  5. WARNING: 'aclocal-1.14' is missing on your system.

    源码安装zabbix agent时进行到make install时报如下错误: WARNING: 'aclocal-1.14' is missing on your system. You shoul ...

  6. MyBatis 分页插件 PageHelper 使用

    1.  引入Maven依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&qu ...

  7. Spring Security OAuth 2.0

    续·前一篇<OAuth 2.0> OAuth 2.0 Provider 实现 在OAuth 2.0中,provider角色事实上是把授权服务和资源服务分开,有时候它们也可能在同一个应用中, ...

  8. 【算法与数据结构专场】BitMap算法基本操作代码实现

    上篇我们讲了BitMap是如何对数据进行存储的,没看过的可以看一下[算法与数据结构专场]BitMap算法介绍 这篇我们来讲一下BitMap这个数据结构的代码实现. 回顾下数据的存储原理 一个二进制位对 ...

  9. 【Java基础】【20IO(字节流)】

    20.01_IO流(IO流概述及其分类) 1.概念 IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的类都在IO包中 流按流向分为两种:输入流,输出流. 流按操 ...

  10. Vim实现分屏

    主要是sp(seismic profile)和vsp(vertical seismic profile)两个命令, 水平拆分 将vm切换到命令模式,  :sp       实现水平拆分屏幕 垂直拆分 ...