Entity Framework 查漏補缺 (三)
Code First的數據庫映射
有兩種方式來實現數據庫映射:
- 數據屬性:Data Annotation
- 映射配置: Fluent API
有繼承關系的實體如何映射?
- Code First在生成數據庫表時,默認使用TPH方式
就是把父類和子類生成同一張表,額外增加了一列Discriminator字段,區分是父類或子類的數據類型
比如:
父類Book對象
public class Book { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public intBookID { get; set; } public string BookName { get; set; } public int Pages { get; set; } }
子類HistoryBooks對象,繼承Book
public class HistoryBooks:Book { public int Chapter { get; set; } }
數據庫生成一張表
- 另一種方式(Data Annotation實現):TPT
不管父類子類,各自生成一張表,以及在子類中增加兩者聯系的外鍵;
定義TPT方式 父類和子類定義 [Table("XXX")]
如:
父類
[Table("Book")] public class Book { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int BookID { get; set; } public string BookName { get; set; } public int Pages { get; set; } }
子類
[Table("HistoryBooks")] public class HistoryBooks:Book {public int Chapter { get; set; } }
映射數據生成的兩張表
Book:
HistoryBooks:
映射方式一:Data Annotation
給實體對象的屬性加上註解特性,實現與數據庫之間建立映射關系並進行控制
如:
Booid映射到表中字段為自增的主鍵
DataAnnotations 包含的常用特性:
KeyAttribute:對應數據庫中表的主鍵的設置
RequiredAttribute:對應數據庫中字段的數據不可null
MaxLengthAttribute:對應數據庫中字符串類型字段的最大長度
ConcurrencyCheckAttribute:指定用於開放式並發檢查的列的數據類型
TimestampAttribute:將列的數據類型指定為行版本
DatabaseGeneratedAttribute:標記指定實體屬性是由數據庫生成的,並指定生成策略(None數據庫不生成值,Identity當插入行時,數據庫生成值,Computed當插入或更新行時,數據庫生成值)
TableAttribute:指定實體類對應的數據表名
ColumnAttribute:指定實體屬性在數據庫中的列名
ForeignKeyAttribute :指定導航屬性的外鍵字段
NotMappeAttribute:不映射對應字段
映射方式二:Fluent API
Fluent API的配置方式可以將實體類與映射配置進行解耦合
有兩種方式來實現Fluent API的映射配置
- 第一種:重寫Dbcontext的中OnModelCreating方法
如下面的Book類,不再有Data Annotation特性
public class Book { public int BookID { get; set; } public string BookName { get; set; } public int Pages { get; set; } }
重寫Dbcontext中的OnModelCreating方法實現Book類映射的配置:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Book>().HasKey(t => t.BookID); base.OnModelCreating(modelBuilder); }
現實項目中,實體對象可能是非常多的,在OnModelCreating方法中逐一進行映射配置,可想而知會造成Dbcontext的代碼龐大。
- 第二種:新建BookMap類,並繼承EntityTypeConfiguration<EntityType>
1、在新建的BookMap類實現映射配置
public class BookMap : EntityTypeConfiguration<Book> { public BookMap() { this.ToTable("Book", "dbo"); this.HasKey(p => p.BookID); //this.HasKey(p => new { p.BookID, p.BookPreID });//關聯主鍵 this.Property(p => p.BookID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);//自動生成 this.Property(p => p.BookName).IsRequired().HasMaxLength(20).HasColumnName("BookName").IsUnicode(false);//非空,最大長度20,自定義列名,列類型為varchar而非nvarchar this.Ignore(p => p.BookDescription);//忽略改屬性的映射 } }
2、依舊重寫Dbcontext中的OnModelCreating方法,將BookMap 類的實例添加到modelBuilder的Configurations。
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new BookMap()); }
這樣能大大減少OnModelCreating的代碼量,依然存在一個問題,就是實體對象一多,還是要逐條將Map類的實例添加到modelBuilder的Configurations
3、利用反射將程序集中所有的EntityTypeConfiguration添加到modelBuilder.Configurations中,可以說完全解耦了
protected override void OnModelCreating(DbModelBuilder modelBuilder) { var typesToRegister = Assembly.GetExecutingAssembly().GetTypes() .Where(type => !String.IsNullOrEmpty(type.Namespace)) .Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); foreach (var type in typesToRegister) { dynamic configurationInstance = Activator.CreateInstance(type); modelBuilder.Configurations.Add(configurationInstance); } }
註:此段代碼源自網友文章,摘自nopCommerce項目的代碼 連接
Entity Framework 查漏補缺 (三)