对EF Code first 编程模型有了一定的了解之后,通过上一篇简单的例子,通过代码对数据库进行了创建,但是其数据结构都是按照EF默认的约定来创建的。比如:
数据库名称:由于上一篇的例子在配置文件中显示设置数据库的连接,因此创建的数据库名称就为连接串中指定的 BookDb。若没有显示指定的话,并使用默认数据库 .\SQLEXPRESS, 则EF生成的数据库名称应该为DbContext 的"命名空间.类名" (DataAccess.BookContext)
表名:默认约定均为模型类名的复数形式。如例子中的 Authors 和 Books ,没有指定架构名称则采用默认架构名:dbo
数据类型:字符串类型默认映射成nvarchar(max),byte[] 数组类型默认映射成varbinary(max),bool类型默认映射成bit,decimal类型默认映射成decimal(18, 2),float类型默认映射成float。由于bool,decimal,float等是值类型,不能为给他们分配Null值。因此生成的数据库会要求对应的列非空。
外键:codefirst 会自动检测模型间的关系,比如我们的实体类中Author 有 List<Books> 属性,Book类中还有 Author 属性,则说明 Author 和Book 是 1对多的关系,EF中外键的命名默认是导航属性名(这里是Author_对应主表的主键)由于我们有了AuthorId ,因此就AuthorId 就默认为外键。
上一篇的例子均是采用EF Code First 的默认配置,下面我们来对这些配置进行修改,有两种方式可以打破默认的配置规则:
- Data Annotations (需引用System.ComponentModel.DataAnnotations),直接作用于类的属性上面。
- 利用FluntAPI,通过增加相应的配置类来覆盖默认配置。
如果使用FluntAPI,需要在上下文类中增加如下方法:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { //这里增加配置代码 base.OnModelCreating(modelBuilder); }
注意 FluntAPI 优先于 Data Annotations 配置。
在《Programming Entity Framework Code First》 这本书中将 配置约定分别分为了三个章节,分别是属性配置、关系配置和数据库映射配置。下面分别对常用的配置做一下总结:
1、表名及架构名:(不指定架构名,则默认为dbo)
Data Annotations 方式:[Table]
[Table("T_Author", Schema = "Lx")] public class Author { public int AuthorId { get; set; } public string Name { get; set; } public DateTime Birthday { get; set; } public byte[] Photo { get; set; } public string Introduce { get; set; } public List<Book> MyBooks { get; set; } }
Flunt API 方式:
modelBuilder.Entity<Author>().ToTable("T_Author", "Lx");
2、字段名:
Data Annotations 方式:[Column]
[Column("AuthorName")] public string Name { get; set; }
Flunt API 方式:
modelBuilder.Entity<Author>().Property(a => a.Name).HasColumnName("AuthorName");
3、主键:
Data Annotations 方式:[Key]
[Key] public int AuthorId { get; set; }
Flunt API 方式:
modelBuilder.Entity<Author>().HasKey(a=>a.AuthorId);
4、外键:
Data Annotations 方式:[ForeignKey]
public int AuthorId { get; set; } [ForeignKey("AuthorId")] public Author Author { get; set; }
注意:如果ForeignKey中指定了列名 ,如“AuthorId ”则类中必须存在 AuthorId 属性
Flunt API 方式:
modelBuilder.Entity<Book>().HasRequired(b => b.Author).WithMany(a => a.MyBooks).HasForeignKey(b => b.AuthorId);
5、设置自动增长属性
利用数据生成项 DatabaseGenerated,它后有三个枚举值: Identity:自增长;None:不处理;Computed:表示这一列是计算列
注意:在EF中,如果主键是int类型,Code First生成数据库的时候会自动设置该列为自增长。但如果主键是Guid类型,需要手动去设置
DATA Annotations 方式:[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int AuthorId { get; set; }
Flunt API 方式:
modelBuilder.Entity<Author>().HasKey(a => a.AuthorId).Property(a => a.AuthorId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
关于 DatabaseGenerated 后面的文章要详细的学习
6、忽略映射
对于不需要映射的表或者列,我们可以忽略映射
Data Annotations 方式:[NotMapped]
//忽略表映射 [NotMapped] public class Book { public int BookId { get; set; } public string BookName { get; set; } public decimal Price { get; set; } public bool IsSellFast { get; set; } public int AuthorId { get; set; } public Author Author { get; set; } }
//忽略列映射 [NotMapped] public bool IsSellFast { get; set; }
Flunt API 方式:
//忽略表映射 modelBuilder.Ignore<Book>();
//忽略列映射 modelBuilder.Entity<Book>().Ignore(b => b.IsSellFast);
7、非空属性及错误提示信息
Data Annotations 方式:[Required] OR [Required(ErrorMessage = "xxx")]
[Required] public string BookName { get; set; } [Required(ErrorMessage = "请输入价格")] public decimal Price { get; set; }
Flunt API 方式:
modelBuilder.Entity<Book>().Property(b => b.BookName).IsRequired();
8、长度:
Data Annotations 方式:[StringLength] 表示长度;[MinLength]表示最小长度;[MaxLength]表示最大长度
[StringLength(10)] public string Name { get; set; } [MinLength(20), MaxLength(200)] public string Introduce { get; set; }
Flunt API 方式 (Flunt Api 方式没有设置 MinLength 这个方法,长度及最大长度都用HasMaxLength 方式)
modelBuilder.Entity<Author>().Property(a => a.Name).HasMaxLength(10);
9、配置类型:
Data Annotations 方式:[Column(TypeName="")]
[Column(TypeName = "image")] public byte[] Photo { get; set; }
Flunt API 方式 :
modelBuilder.Entity<Book>().Property(b => b.Price).HasColumnType("image");
10、防止并发:
(1)对表做并发处理:
Data Annotations 方式:[Timestamp]
注意:如果要对某一个表做并发处理,就在该表中加一条Timestamp类型的字段,但是这个字段必须为byte[]类型,并且一个类中只能存在一个。
[Timestamp] public byte[] RowVersion { get; set; }
Flunt API 方式 :
modelBuilder.Entity<Book>().Property(b => b.RowVersion).IsRowVersion();
(2)对某个字段作并发控制:
Data Annotations 方式:[ConcurrencyCheck]
[ConcurrencyCheck] public string BookName { get; set; }
Flunt API 方式 :
modelBuilder.Entity<Book>().Property(b => b.BookName).IsConcurrencyToken();
关于 并发控制 后面的文章要详细的学习
10、复杂类型
Data Annotations 方式:[ComplexType]
[ComplexType] public class Address { public int AddressId { get; set; } public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
Flunt API 方式 :
modelBuilder.ComplexType<Address>();
关于 复杂类型 后面的文章要详细的学习
这里只列举和记录了 10 种配置方式,具体参见: http://msdn.microsoft.com/zh-cn/library/system.componentmodel.dataannotations.aspx
下面把先前的例子的某些配置整理一下:
Data Annotations 方式:
//设置Author 类 映射表名为 T_Author,架构名为 Lx [Table("T_Author",Schema="Lx")] public class Author { [Column("AuthorId",TypeName="int")] public int AuthorId { get; set; } //修改Name属性 映射为 AuthorName //长度 10 [Column("AuthorName",TypeName="nvarchar")] [Required,StringLength(10)] public string Name { get; set; } //设置Birthday 可空 public DateTime? Birthday { get; set; } //设置 Photo 属性 映射类型 为 image [Column(TypeName = "image")] public byte[] Photo { get; set; } //设置 Introduce 属性最小长度 20,最大长度 100 [MinLength(20),MaxLength(100)] public string Introduce { get; set; } public List<Book> MyBooks { get; set; } } //设置 Book 类 映射表名为 T_Books,架构名为 Lx [Table("T_Books", Schema = "Lx")] public class Book { // BookId 为主键且自增 [Key] public int BookId { get; set; } public int AuthorId { get; set; } //设置 BookName 属性 不能为空,字段长度为 50 [Required,StringLength(50)] public string BookName { get; set; } //DataAnnotation 方式不能控制deciaml 的精度 //默认为(18,2) public decimal Price { get; set; } public bool IsSellFast { get; set; } //设置外键为 AuthorId [ForeignKey("AuthorId")] public Author Author { get; set; } }
Flunt API 方式 :
protected override void OnModelCreating(DbModelBuilder modelBuilder) { //设置 Author 表名 T_Author 架构名 Lx modelBuilder.Entity<Author>().ToTable("T_Authors", "Lx"); //设置 Name 属性 非空,映射为列名 AuthorName,长度 10 modelBuilder.Entity<Author>().Property(a => a.Name).IsRequired() .HasColumnName("AuthorName") .HasMaxLength(10); //设置 photo 属性 映射类型为 image modelBuilder.Entity<Author>().Property(a => a.Photo).HasColumnType("image"); //Flunt Api 无法设置最小长度 modelBuilder.Entity<Author>().Property(a => a.Introduce).HasMaxLength(100); //设置 Book 表名 T_Books 架构名 Lx modelBuilder.Entity<Book>().ToTable("T_Books", "Lx"); //设置 BookId 为主键且自动增长 modelBuilder.Entity<Book>().HasKey(b => b.BookId) .Property(b => b.BookId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); //设置 AuthorId 为外键 modelBuilder.Entity<Book>().HasRequired(b => b.Author).WithMany(a => a.MyBooks).HasForeignKey(b => b.AuthorId);
//设置 BookName 属性不能为空,字符串长度 50 modelBuilder.Entity<Book>().Property(b => b.BookName).IsRequired() .HasMaxLength(50); //FluntAPI 可以设置 decimal 类型的精度 modelBuilder.Entity<Book>().Property(b => b.Price).HasColumnType("decimal").HasPrecision(3, 1);
}
运行程序,结果如下图: