深入了解EntityFramework——Fluent API
Fluent API??
除了慣例原則與屬性數(shù)據(jù)注解外,FluentAPI是另一種支持實(shí)體類配置設(shè)置的方式。與屬性數(shù)據(jù)注解相比,它提供了更廣泛的功能與設(shè)置彈性。實(shí)體類若同時(shí)設(shè)置了數(shù)據(jù)注解,則采用的優(yōu)先權(quán)是“Fluent API” > "數(shù)據(jù)注解" > "慣例"。一旦設(shè)置了Fluent API 無(wú)論數(shù)據(jù)注解還是慣例規(guī)則均會(huì)被覆蓋。
DbContext類定義的OnModelCreating 方法是最常調(diào)用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方法 | 作用 |
| 架構(gòu)相關(guān)配置 | HasDefaultSchema() | 數(shù)據(jù)庫(kù)的默認(rèn)架構(gòu) |
| ComplexType() | 把一個(gè)類配置為復(fù)雜類型 | |
| 實(shí)體相關(guān)配置 | HasIndex() | 實(shí)體的的索引 |
| HasKey() | 實(shí)體的主鍵(可其實(shí)現(xiàn)復(fù)合主鍵,[Key]在EF?core中不能實(shí)現(xiàn)復(fù)合主鍵) | |
| HasMany() | 1對(duì)多的或者?多對(duì)多關(guān)系? | |
| HasOptional() | 一個(gè)可選的關(guān)系,這樣配置會(huì)在數(shù)據(jù)庫(kù)中生成一個(gè)可空的外鍵 | |
| HasRequired() | 一個(gè)必有的關(guān)系,這樣配置會(huì)在數(shù)據(jù)庫(kù)中生成一個(gè)不能為空的外鍵 | |
| Ignore() | 實(shí)體或者實(shí)體的屬性不映射到數(shù)據(jù)庫(kù) | |
| Map() | 設(shè)置一些優(yōu)先的配置 | |
| MapToStoredProcedures() | 實(shí)體的CUD操作使用存儲(chǔ)過(guò)程 | |
| ToTable() | 為實(shí)體設(shè)置表名 | |
| 屬性相關(guān)配置 | HasColumnAnnotation() | 給屬性設(shè)置注釋 |
| IsRequired() | 在調(diào)用SaveChanges()方法時(shí),屬性不能為空 | |
| IsOptional() | 可選的,在數(shù)據(jù)庫(kù)生成可空的列 | |
| HasParameterName() | 配置用于該屬性的存儲(chǔ)過(guò)程的參數(shù)名 | |
| HasDatabaseGeneratedOption() | 配置數(shù)據(jù)庫(kù)中對(duì)應(yīng)列的值怎樣生成的,如計(jì)算,自增等 | |
| HasColumnOrder() | 配置數(shù)據(jù)庫(kù)中對(duì)應(yīng)列的排列順序 | |
| HasColumnType() | 配置數(shù)據(jù)庫(kù)中對(duì)應(yīng)列的數(shù)據(jù)類型 | |
| HasColumnName() | 配置數(shù)據(jù)庫(kù)中對(duì)應(yīng)列的列名 | |
| IsConcurrencyToken() | 配置數(shù)據(jù)庫(kù)中對(duì)應(yīng)列用于樂(lè)觀并發(fā)檢測(cè) |
舉一個(gè)簡(jiǎn)單例子,?新建一個(gè)FluentApiDemo的控制臺(tái)應(yīng)用程序,添加Product實(shí)體類和上下文類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函數(shù)中的代碼如下圖所示:
class Program{static void Main(string[] args){using (KTStoreModel db = new KTStoreModel()){Console.Write("count:{0}",db.Product.Count());Console.ReadKey();}}}運(yùn)行后在數(shù)據(jù)庫(kù)生成的表結(jié)果如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
?CodeFirst配置一對(duì)一,一對(duì)多,多對(duì)多關(guān)系
產(chǎn)品類Product和書(shū)本類Book是典型的一對(duì)一關(guān)系,產(chǎn)品中可能有書(shū)本這個(gè)屬性,也可能沒(méi)書(shū)本這個(gè)屬性。但是書(shū)本必屬于產(chǎn)品的一部分。
public class Product{public int Id { get; set; }public string Name { get; set;}public int CategoryId { get; set; }public int Price { get; set; }//導(dǎo)航屬性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; }//導(dǎo)航屬性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設(shè)置可空的Book屬性.WithRequired(e => e.Product);//給Book設(shè)置不能為空的Product屬性,沒(méi)有Book屬性時(shí)不能保存}public virtual DbSet<Product> Product { get; set; }public virtual DbSet<Book> Book { get; set; }}生成的數(shù)據(jù)表結(jié)構(gòu)如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ???
?
書(shū)本類Book和作者類Author是典型的多對(duì)多關(guān)系,一本書(shū)可能有多個(gè)作者,一個(gè)作者可能寫(xiě)了多本書(shū)。?
public class Book{public int Id { get; set; }public int Pages { get; set; }public string ISBN { get; set; }public string Publisher { get; set; }//導(dǎo)航屬性public virtual List<Author> Author { get; set; }} public class Author{public int Id { get; set; }public string Name { get; set; }//導(dǎo)航屬性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)//配置一個(gè)作者有多本書(shū).WithMany(e => e.Author)//配置一本書(shū)有多個(gè)作者.Map(m => m.ToTable("BookAuthor")//生成BookAuthor中間表.MapLeftKey("AuthorId")//因?yàn)槭峭ㄟ^(guò)Entity<Author>開(kāi)始的,所以左表是Author.MapRightKey("BookId"));//又表是BookId}public virtual DbSet<Book> Book { get; set; }public virtual DbSet<Author> Author { get; set; }}生成的數(shù)據(jù)庫(kù)表結(jié)構(gòu)如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
多對(duì)多關(guān)聯(lián)——附加數(shù)據(jù)字段?
還有一種多對(duì)多關(guān)聯(lián)的情況是,存儲(chǔ)兩個(gè)數(shù)據(jù)表關(guān)聯(lián)信息的數(shù)據(jù)庫(kù)表?本身具有其他有效的數(shù)據(jù)字段。這種情況下,實(shí)體數(shù)據(jù)模型必須為此數(shù)據(jù)表建立起專用的數(shù)據(jù)類型。
?產(chǎn)品Product類和訂單Order類是一個(gè)多對(duì)多的關(guān)系,OrderDetail存儲(chǔ)兩個(gè)數(shù)據(jù)表的關(guān)聯(lián)信息,并且OrderDetail表中存在其他數(shù)據(jù)字段。
public class Product{public int Id { get; set; }public string Name { get; set;}public int Price { get; set; }//導(dǎo)航屬性public virtual List<OrderDetail> OrderDetail { get; set; } } public class Order{public int Id { get; set; }public DateTime OrderDate { get; set; }//導(dǎo)航屬性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; }//導(dǎo)航屬性public virtual Product Product { get; set; }public virtual Order Order { get; set; }}?運(yùn)行后建立的數(shù)據(jù)庫(kù)表結(jié)構(gòu)如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?
訂單系統(tǒng)中,訂單主表和明細(xì)表是典型的一對(duì)多關(guān)系。?
public class Order{public int Id { get; set; }public DateTime OrderDate { get; set; }//導(dǎo)航屬性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; }//導(dǎo)航屬性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>();//取消數(shù)據(jù)庫(kù)表復(fù)數(shù)形式modelBuilder.Entity<OrderDetail>().HasRequired(od => od.Order)//OrderDetail有必須要的導(dǎo)航屬性O(shè)rder.WithMany(o => o.OrderDetail)//OrderDetail有集體導(dǎo)航屬性O(shè)rderDetail.HasForeignKey(x => x.XOrderId);//設(shè)置外鍵 }public virtual DbSet<Order> Order { get; set; }public virtual DbSet<OrderDetail> OrderDetail { get; set; }}?也可以使用Fluent API 反向關(guān)聯(lián),代碼如下:
modelBuilder.Entity<Order>().HasMany(o => o.OrderDetail).WithRequired(od => od.Order).HasForeignKey(od => od.XOrderId);運(yùn)行后建立的數(shù)據(jù)庫(kù)表結(jié)構(gòu)如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?級(jí)聯(lián)刪除
級(jí)聯(lián)刪除是指父級(jí)記錄刪除時(shí)會(huì)自動(dòng)刪除子級(jí)記錄。比如訂單主表刪除后自動(dòng)刪除訂單從表。
在EF中,默認(rèn)是打開(kāi)級(jí)聯(lián)刪除的。
一對(duì)多:如訂單主表刪除時(shí),訂單從表從中外鍵OrderId變成null。
一對(duì)一:如刪除Product時(shí),對(duì)應(yīng)的Book也會(huì)刪除。
多對(duì)多:如刪除一種book信息時(shí),在中間表中對(duì)應(yīng)的作者信息也會(huì)刪除。
代碼如下圖所示:
modelBuilder.Entity<OrderDetail>().HasRequired(od => od.Order).WithMany(o => o.OrderDetail).HasForeignKey(x => x.XOrderId).WillCascadeOnDelete();//開(kāi)啟級(jí)聯(lián)刪除modelBuilder.Entity<Order>().HasMany(o => o.OrderDetail).WithRequired(od => od.Order).HasForeignKey(od => od.XOrderId).WillCascadeOnDelete(false);//關(guān)閉級(jí)聯(lián)刪除配置單個(gè)實(shí)體
我們已經(jīng)知道了在OnModelCreating()方法中可以通過(guò)FluentApi對(duì)所有的實(shí)體類進(jìn)行配置,然而當(dāng)實(shí)體類很多時(shí),我們把所有的配置都放在OnModelCreating()方法中很難維護(hù)。EF6允許我們給每一個(gè)實(shí)體添加一個(gè)單獨(dú)的配置類,通過(guò)這個(gè)配置類來(lái)對(duì)相應(yīng)的實(shí)體進(jìn)行配置。
以配置Student實(shí)體類為例,我們?cè)贠nModelCreating()方法中配置Student實(shí)體,代碼如下:
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");});} }我們可以將每個(gè)實(shí)體類的配置放在一個(gè)對(duì)應(yīng)的的配置類,(如Studnet的實(shí)體配置在StudentEntityConfiguratinos配置類中),如果程序中有很多實(shí)體類,采用單獨(dú)配置的方式可以很好的提高配置的可維護(hù)性和可讀性。
?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實(shí)體的配置modelBuilder.Configurations.Add(new StudentEntityConfiguration());} }?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的深入了解EntityFramework——Fluent API的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java 验证码校验_JavaWeb验证
- 下一篇: 《计算机算法设计与分析》题目汇总