EFCodeFirst模式使用的是约定大于配置的编程模式,这种模式利用默认约定根据咱们的领域模型创建概念模型。而后咱们也能够经过配置领域类来覆盖默认约定。web
覆盖默认约定主要用两种手段:sql
1.数据注释属性(Data Annotations Attributes)数据库
2.FluentAPI编程
咱们能够给领域类或者类的属性添加数据注释属性来实现覆盖默认约定,其实在MVC webApi中也会常常用到数据注释属性。一个栗子架构
[Table("StudentInfo",Schema ="MySchool")]//数据库名为MySchool.StudentInfo public class Student { public Student() { } [Key]//默认把Id或者classNameId做为主键。经过[key]能够指定主键,若是是数字类型,默认自增 public int SID { get; set; } [Column("Name",Order=1,TypeName="ntext")]//必有项:列名为Name ;非必有项:顺序为1,数据库中类型为ntext(order是从0开始的) [MaxLength(20)] public string StudentName { get; set; } [NotMapped]//不映射到数据库中 public int? Age { get; set; } public string Phone{get{return "13545678901";}}//getter,setter有一个不存在,就不会映射到数据库
[Index("IndexName",IsClustered =true ,IsUnique =true)]//索引名为IndexName,是汇集索引和惟一索引,直接使用[index]时索引名为IX_StudentNo,为非汇集索引 public string StudentNo{get;set;}
[Timestamp]//在update操做时,包含在where子句中 public byte[] RowVersion{get;set;} public int StdId { get; set; } [ForeignKey("StdId")] public virtual Standard Standard { get; set; } }
一些经常使用的数据注释属性并发
Key | 数据库中对应列为主键 |
Timestamp | 数据库中对应列为timestamp类型,主要用于解决高并发问题app 注:一个类只能用一次,且修饰的属性必须为byte[]类型ide |
ConcurrencyCheck | 数据库中对应列进行乐观并发检测,主要用于解决高并发问题 |
Required | 属性不为空,数据中对应列 |
MinLength/MaxLength | 属性和数据库中的最小/最大的string长度 |
StringLength | 属性和数据库中的最小,最大 |
架构属性高并发
Table | 用于实体,配置实体对应的数据库表名和表结构 |
Column | 用于属性,配置属性对应数据库列名,顺序和数据类型 |
Index | 用于属性,配置对应数据库的列为索引 |
ForeignKey | 用于属性,指定属性为一个外键 |
NotMapped | 用于实体/属性,不在数据库中生成映射 |
DataBaseGernerated | 用于属性,设置数据库对应列值的生成,如identity,computed或者none |
EF6中经过Key和Column属性能够实现复合主键,一个栗子:ui
public class Student { [Key] [Column(Order=1)] public int StudentKey { get; set; } [Key] [Column(Order=2)] public int AdmissionNum { get; set; } public string StudentName { get; set; } }
数据库以下:
注意:EF core中不支持经过[key]属性来设置复合主键,咱们能够经过Fluent API的HasKey()方法来实现。
一个栗子:
public class 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 int StandardId { get; set; } public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }
在上边的栗子中,没有使用ForeignKey覆盖默认的约定,那么安照默认约定执行:EF找到导航属性Standard,找到Standard的主键为StandardId,正好Student类中有StandardId属性,那么就把StandardId设为外键;若是Student类没有StandardId呢?EF会建立一个外键<导航属性名>_<导航属性主键>,在本例中就是Standard_StandardId。
咱们不想使用EF自动建立的外键,而是想把一个属性设置成外键,可是这个属性和导航属性的主键名不一致,咱们应该怎么去实现呢?
1.[ForeignKey(导航属性名)] 依赖实体(Student依赖Standard,因此Student是依赖实体)中在咱们想设置为外键的属性上指定导航属性
public class Student { public int StudentID { get; set; } public string StudentName { get; set; } [ForeignKey("Standard")] public int StandardRefId { get; set; } public Standard Standard { get; set; } } public class Standard { public int StandardId { get; set; } public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }
2.[ForeignKey(导航属性名)] 依赖实体中在导航属性上指定属性名
public class Student { public int StudentID { get; set; } public string StudentName { get; set; } public int StandardRefId { get; set; } [ForeignKey("StandardRefId")] public Standard Standard { get; set; } } public class Standard { public int StandardId { get; set; } public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }
3.主实体中在导航属性上指定属性名(不推荐)
ublic class Student { public int StudentID { get; set; } public string StudentName { get; set; } public int StandardRefId { get; set; } public Standard Standard { get; set; } } public class Standard { public int StandardId { get; set; } public string StandardName { get; set; } [ForeignKey("StandardRefId")] public ICollection<Student> Students { get; set; } }
ConcurrencyCheck属性能够用在领域类的一个或多个属性中,领域类的属性使用它修饰后,数据库中对应的列会启用乐观并发检测。
一个栗子:
public class Student { public int StudentId { get; set; } [ConcurrencyCheck] public string StudentName { get; set; } }
上边栗子中StudentName使用ConcurrencyCheck注释,EF会在Update时将StudentName添加到where子句中。
using(var context = new SchoolContext()) { var std = new Student() { StudentName = "Bill" }; context.Students.Add(std); context.SaveChanges(); std.StudentName = "Steve"; context.SaveChanges(); }
在执行SaveChanges()方法时,执行的Sql命令以下:
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只能用于byte[]类型,且一个类中只能用一次,而ConcurrencyCheck用于任何数据类型的属性且能在一个类中使用屡次。