深入了解EntityFramework——Fluent API
Fluent API??
除了慣例原則與屬性數據注解外,FluentAPI是另一種支持實體類配置設置的方式。與屬性數據注解相比,它提供了更廣泛的功能與設置彈性。實體類若同時設置了數據注解,則采用的優先權是“Fluent API” > "數據注解" > "慣例"。一旦設置了Fluent API 無論數據注解還是慣例規則均會被覆蓋。
DbContext類定義的OnModelCreating 方法是最常調用FLuent API的地方,如下圖所示:
public class SchoolContext: DbContext {public KTStoreContext():base("name=KTStore"){}public DbSet<Product> Products { get; set; }protected override void OnModelCreating(DbModelBuilder modelBuilder){//Write Fluent API configurations here} }?
| 配置 | Fluent API方法 | 作用 |
| 架構相關配置 | HasDefaultSchema() | 數據庫的默認架構 |
| ComplexType() | 把一個類配置為復雜類型 | |
| 實體相關配置 | HasIndex() | 實體的的索引 |
| HasKey() | 實體的主鍵(可其實現復合主鍵,[Key]在EF?core中不能實現復合主鍵) | |
| HasMany() | 1對多的或者?多對多關系? | |
| HasOptional() | 一個可選的關系,這樣配置會在數據庫中生成一個可空的外鍵 | |
| HasRequired() | 一個必有的關系,這樣配置會在數據庫中生成一個不能為空的外鍵 | |
| Ignore() | 實體或者實體的屬性不映射到數據庫 | |
| Map() | 設置一些優先的配置 | |
| MapToStoredProcedures() | 實體的CUD操作使用存儲過程 | |
| ToTable() | 為實體設置表名 | |
| 屬性相關配置 | HasColumnAnnotation() | 給屬性設置注釋 |
| IsRequired() | 在調用SaveChanges()方法時,屬性不能為空 | |
| IsOptional() | 可選的,在數據庫生成可空的列 | |
| HasParameterName() | 配置用于該屬性的存儲過程的參數名 | |
| HasDatabaseGeneratedOption() | 配置數據庫中對應列的值怎樣生成的,如計算,自增等 | |
| HasColumnOrder() | 配置數據庫中對應列的排列順序 | |
| HasColumnType() | 配置數據庫中對應列的數據類型 | |
| HasColumnName() | 配置數據庫中對應列的列名 | |
| IsConcurrencyToken() | 配置數據庫中對應列用于樂觀并發檢測 |
舉一個簡單例子,?新建一個FluentApiDemo的控制臺應用程序,添加Product實體類和上下文類KTStoreModel,如下圖所示:
namespace FluentApiDemo {public class Product{public int XPId { get; set; }public string Name { get; set; }public int Price { get; set; }public int SPrice { get; set; }public string Category { get; set; }} } public class KTStoreModel : DbContext{public KTStoreModel(): base("name=KTStoreModel"){}protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Entity<Product>().ToTable("tbProduct");modelBuilder.Entity<Product>().HasKey(p => p.XPId);modelBuilder.Entity<Product>().Property(p => p.Name).HasColumnName("ProductName");modelBuilder.Entity<Product>().Property(p => p.Name).HasMaxLength(50);modelBuilder.Entity<Product>().Property(p => p.Name).IsRequired();modelBuilder.Entity<Product>().Property(p => p.Price).HasColumnName("ProductPrice");modelBuilder.Entity<Product>().Ignore(p => p.SPrice);base.OnModelCreating(modelBuilder);}public virtual DbSet<Product> Product { get; set; }}Main函數中的代碼如下圖所示:
class Program{static void Main(string[] args){using (KTStoreModel db = new KTStoreModel()){Console.Write("count:{0}",db.Product.Count());Console.ReadKey();}}}運行后在數據庫生成的表結果如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
?CodeFirst配置一對一,一對多,多對多關系
產品類Product和書本類Book是典型的一對一關系,產品中可能有書本這個屬性,也可能沒書本這個屬性。但是書本必屬于產品的一部分。
public class Product{public int Id { get; set; }public string Name { get; set;}public int CategoryId { get; set; }public int Price { get; set; }//導航屬性public virtual Book Book { get; set; } } public class Book{public int Id { get; set; }public int Pages { get; set; }public string ISBN { get; set; }public string Author { get; set; }public string Publisher { get; set; }//導航屬性public virtual Product Product { get; set; }}?使用Fluent API配置如下:
public class kTStoreModel : DbContext{public kTStoreModel(): base("name=kTStoreModel"){}protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Entity<Product>().HasOptional(e => e.Book)//給Product設置可空的Book屬性.WithRequired(e => e.Product);//給Book設置不能為空的Product屬性,沒有Book屬性時不能保存}public virtual DbSet<Product> Product { get; set; }public virtual DbSet<Book> Book { get; set; }}生成的數據表結構如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ???
?
書本類Book和作者類Author是典型的多對多關系,一本書可能有多個作者,一個作者可能寫了多本書。?
public class Book{public int Id { get; set; }public int Pages { get; set; }public string ISBN { get; set; }public string Publisher { get; set; }//導航屬性public virtual List<Author> Author { get; set; }} public class Author{public int Id { get; set; }public string Name { get; set; }//導航屬性public virtual List<Book> Book { get; set; }}??使用Fluent API配置如下:
public class kTStoreModel : DbContext{public kTStoreModel(): base("name=kTStoreModel"){}protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Entity<Author>().HasMany(e => e.Book)//配置一個作者有多本書.WithMany(e => e.Author)//配置一本書有多個作者.Map(m => m.ToTable("BookAuthor")//生成BookAuthor中間表.MapLeftKey("AuthorId")//因為是通過Entity<Author>開始的,所以左表是Author.MapRightKey("BookId"));//又表是BookId}public virtual DbSet<Book> Book { get; set; }public virtual DbSet<Author> Author { get; set; }}生成的數據庫表結構如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
多對多關聯——附加數據字段?
還有一種多對多關聯的情況是,存儲兩個數據表關聯信息的數據庫表?本身具有其他有效的數據字段。這種情況下,實體數據模型必須為此數據表建立起專用的數據類型。
?產品Product類和訂單Order類是一個多對多的關系,OrderDetail存儲兩個數據表的關聯信息,并且OrderDetail表中存在其他數據字段。
public class Product{public int Id { get; set; }public string Name { get; set;}public int Price { get; set; }//導航屬性public virtual List<OrderDetail> OrderDetail { get; set; } } public class Order{public int Id { get; set; }public DateTime OrderDate { get; set; }//導航屬性public virtual List<OrderDetail> OrderDetail { get; set; }} public class OrderDetail{[Key][Column(Order = 0)][DatabaseGenerated(DatabaseGeneratedOption.None)]public int OrderId { get; set; }[Key][Column(Order = 1)][DatabaseGenerated(DatabaseGeneratedOption.None)]public int ProductId { get; set; }public int Quantity { get; set; }public int Price { get; set; }//導航屬性public virtual Product Product { get; set; }public virtual Order Order { get; set; }}?運行后建立的數據庫表結構如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?
訂單系統中,訂單主表和明細表是典型的一對多關系。?
public class Order{public int Id { get; set; }public DateTime OrderDate { get; set; }//導航屬性public virtual List<OrderDetail> OrderDetail { get; set; }} public class OrderDetail{public int Id { get; set; }public string ProductName { get; set; }public int Quantity { get; set; }public int Price { get; set; }public int XOrderId { get; set; }//導航屬性public virtual Order Order { get; set; }}使用Fluent API配置如下:
public class kTStoreModel : DbContext{public kTStoreModel(): base("name=kTStoreModel"){}protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();//取消數據庫表復數形式modelBuilder.Entity<OrderDetail>().HasRequired(od => od.Order)//OrderDetail有必須要的導航屬性Order.WithMany(o => o.OrderDetail)//OrderDetail有集體導航屬性OrderDetail.HasForeignKey(x => x.XOrderId);//設置外鍵 }public virtual DbSet<Order> Order { get; set; }public virtual DbSet<OrderDetail> OrderDetail { get; set; }}?也可以使用Fluent API 反向關聯,代碼如下:
modelBuilder.Entity<Order>().HasMany(o => o.OrderDetail).WithRequired(od => od.Order).HasForeignKey(od => od.XOrderId);運行后建立的數據庫表結構如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?級聯刪除
級聯刪除是指父級記錄刪除時會自動刪除子級記錄。比如訂單主表刪除后自動刪除訂單從表。
在EF中,默認是打開級聯刪除的。
一對多:如訂單主表刪除時,訂單從表從中外鍵OrderId變成null。
一對一:如刪除Product時,對應的Book也會刪除。
多對多:如刪除一種book信息時,在中間表中對應的作者信息也會刪除。
代碼如下圖所示:
modelBuilder.Entity<OrderDetail>().HasRequired(od => od.Order).WithMany(o => o.OrderDetail).HasForeignKey(x => x.XOrderId).WillCascadeOnDelete();//開啟級聯刪除modelBuilder.Entity<Order>().HasMany(o => o.OrderDetail).WithRequired(od => od.Order).HasForeignKey(od => od.XOrderId).WillCascadeOnDelete(false);//關閉級聯刪除配置單個實體
我們已經知道了在OnModelCreating()方法中可以通過FluentApi對所有的實體類進行配置,然而當實體類很多時,我們把所有的配置都放在OnModelCreating()方法中很難維護。EF6允許我們給每一個實體添加一個單獨的配置類,通過這個配置類來對相應的實體進行配置。
以配置Student實體類為例,我們在OnModelCreating()方法中配置Student實體,代碼如下:
public DbSet<Student> Students { get; set; }protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Entity<Student>().ToTable("StudentInfo");modelBuilder.Entity<Student>().HasKey<int>(s => s.StudentKey);modelBuilder.Entity<Student>().Property(p => p.DateOfBirth).HasColumnName("Birthday").HasColumnOrder(3).HasColumnType("datetime2");modelBuilder.Entity<Student>().Property(p => p.StudentName).HasMaxLength(50);modelBuilder.Entity<Student>().Property(p => p.StudentName).IsConcurrencyToken();modelBuilder.Entity<Student>().HasMany<Course>(s => s.Courses).WithMany(c => c.Students).Map(cs =>{cs.MapLeftKey("StudentId");cs.MapRightKey("CourseId");cs.ToTable("StudentCourse");});} }我們可以將每個實體類的配置放在一個對應的的配置類,(如Studnet的實體配置在StudentEntityConfiguratinos配置類中),如果程序中有很多實體類,采用單獨配置的方式可以很好的提高配置的可維護性和可讀性。
?StudentEntityConfiguratinos類需要繼承EntityTypeConfiguration<TEntity>:
public class StudentEntityConfiguration: EntityTypeConfiguration<Student> {public StudentEntityConfiguration(){this.ToTable("StudentInfo");this.HasKey<int>(s => s.StudentKey);this.Property(p => p.DateOfBirth).HasColumnName("DoB").HasColumnOrder(3).HasColumnType("datetime2");this.Property(p => p.StudentName).HasMaxLength(50);this.Property(p => p.StudentName).IsConcurrencyToken();this.HasMany<Course>(s => s.Courses).WithMany(c => c.Students).Map(cs =>{cs.MapLeftKey("StudentId");cs.MapRightKey("CourseId");cs.ToTable("StudentCourse");});} }在OnModelCreating()方法中使用上邊的配置類:
public class SchoolDBContext: DbContext {public SchoolDBContext(): base() {}public DbSet<Student> Students { get; set; }protected override void OnModelCreating(DbModelBuilder modelBuilder){// 添加Student實體的配置modelBuilder.Configurations.Add(new StudentEntityConfiguration());} }?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的深入了解EntityFramework——Fluent API的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 验证码校验_JavaWeb验证
- 下一篇: java重命名package_Andro