EF6學習筆記四:一對多、多對多、一對一關係配置(Fluent API)
要專業系統地學習EF前往《你必須掌握的Entity Framework 6.x與Core 2.0》這本書的作者(汪鵬,Jeffcky)的部落格:https://www.cnblogs.com/CreateMyself/
現在就來到了重中之重的配置了:一對多、多對多、一對一關係的配置,我這裡全部使用Fulent API 的方式來
一對多
一對多最簡單了,寫好你的資料模型,什麼配置都不用做,生成的表結構就是你想要的
來個簡單基類
public class BaseEntity { public BaseEntity() {View Codethis.Id = Guid.NewGuid().ToString(); this.AddTime = DateTime.Now; } public string Id { get; set; } public DateTime AddTime { get; set; } }
來個簡單圖書館類
public class Library:BaseEntity { public string Name { get; setView Code; } public virtual ICollection<Book> Books { get; set; } }
來個簡單Book類
public class Book:BaseEntity { public string Name { get; set; } public virtual Library Library { get; set; } }View Code
為他們公開DbSet屬性
public DbSet<Library> Librarys { get; set; } public DbSet<Book> Books { get; set; }View Code
然後生成的表就是這樣的
在Book中有一個Library的外來鍵,這是預設給生成的,你可能覺得這個外來鍵名呢想以“FK_”開頭,還有表名你也不滿意,那行啊,來配置啊
咱們現在的配置就來專業一點,我們把對每一個model的配置單獨用一個類,最後通過反射的方式,把配置新增到上下文的OnModelCreating方法中
你的OnModelCreating中只需要這樣寫,就行了,上一篇我弄錯了
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); var typeToRegister = Assembly.GetExecutingAssembly().GetTypes() .Where(x => !string.IsNullOrEmpty(x.Namespace)) .Where(x => x.BaseType != null && x.BaseType.IsGenericType && x.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); foreach (var type in typeToRegister) { dynamic configurationInstance = Activator.CreateInstance(type); modelBuilder.Configurations.Add(configurationInstance); } base.OnModelCreating(modelBuilder); }View Code
你的配置程式碼寫在哪裡呢?請看我的專案檔案結構
實體和配置分開放,便於管理,上下文裡面你已經不用去管了
來看具體的配置
public class LibraryMap:EntityTypeConfiguration<Library> { public LibraryMap() { ToTable("tb_Librarys"); // 表名 HasKey(x => x.Id); // 主鍵 Property(x => x.Name); HasMany(x => x.Books).WithRequired(x => x.Library).HasForeignKey(x => x.FK_Library_Id); // 從圖書館的角度配置,一個圖書館有多本書,一本書必須屬於某一個圖書館,也可以從圖書的角度去配置 } }View Code
public class BookMap:EntityTypeConfiguration<Book> { public BookMap() { ToTable("tb_Books"); HasKey(x => x.Id); // 從圖書的角度來配置是這樣的 // HasRequired(x => x.Library).WithMany(x => x.Books).HasForeignKey(x => x.FK_Library_Id); } }View Code
現在生成的表就是我們想要的了
多對多
多對多也簡單,用學生和課程來舉例,一個學生可以學習多門課程,一門課程也可以供多名學生來學習
多對多的關係,在資料庫中我們需要建立第三張表專門維護兩者的關係。其中有兩種方法,通過配置 ,自動建立第三張表;或者你顯示的建立第三model
public class Student500:BaseEntity { public string Name { get; set; } public virtual ICollection<Course500> Courses500 { get; set; } }View Code
public class Course500:BaseEntity { public string Name { get; set; } public virtual ICollection<Student500> Students500 { get; set; } }View Code
public class Stduent500Map:EntityTypeConfiguration<Student500> { public Stduent500Map() { ToTable("tb_Students500"); HasKey(x => x.Id); HasMany(x => x.Courses500).WithMany(x => x.Students500) .Map(x => x.ToTable("tb_StudentCourses500") .MapLeftKey("Stduent500Id") .MapRightKey("Course500Id")); } }View Code
public class Course500Map:EntityTypeConfiguration<Course500> { public Course500Map() { ToTable("tb_Courses500"); HasKey(x =>x.Id); Property(x => x.Name); } }View Code
生成的表結構是這樣的
這應該是比較符合我們得期望了,我們來看一下顯示地建立第三個model,怎麼弄。上面生成的StudentCourses500,他沒有自己的Id和AddTime,那麼我們現在這樣做就行了
顯示定義第三個表來維護student和course的關係
public class Student100:BaseEntity { public string Name { get; set; } public byte Age { get; set; } public virtual ICollection<StudentCourse100> StudentCourse { get; set; } }View Code
public class Course100:BaseEntity { public string Name { get; set; } public int ManimumStrength { get; set; } public virtual ICollection<StudentCourse100> StudentCourse { get; set; } }View Code
public class StudentCourse100:BaseEntity { public string Student100Id { get; set; } public virtual Student100 Student100 { get; set; } public string Courses100Id { get; set; } public virtual Course100 Course100 { get; set; } }View Code
public class Student100Map:EntityTypeConfiguration<Student100> { public Student100Map() { ToTable("tb_Student100"); HasKey(x => x.Id); HasMany(x => x.StudentCourse) .WithRequired(x => x.Student100) .HasForeignKey(x => x.Student100Id); } }View Code
public class Course100Map:EntityTypeConfiguration<Course100> { public Course100Map() { ToTable("tb_Course100"); HasKey(x => x.Id); HasMany(x => x.StudentCourse) .WithRequired(x => x.Course100) .HasForeignKey(x => x.Courses100Id); } }View Code
表結構如下
顯示定義第三張表,我們對這張表就有了更多的設定,只不過,新增資料時會有點繞
db.Students100.Add(new Student100 { Name = "王五", Age = 44, StudentCourse = new List<StudentCourse100> { new StudentCourse100{ Course100 = new Course100{ Name="生物",ManimumStrength=45} }, new StudentCourse100{ Course100 = new Course100{ Name="化學",ManimumStrength=45} }, new StudentCourse100{ Course100 = new Course100{ Name="歷史",ManimumStrength=45} } } }); db.SaveChanges();View Code
一對一
弄這個之前我來引用一段《你必須知道的.Net Framework 6.x 與 Core 2.0》作者的原話:“正常情況下,首先應該探討一對一關係。但是如果使用過一對一關係,就會發現並不是那麼簡單,換句話說,一對一關係最為複雜,設計如何使用HasOptional 與 WithRequired 、WithOptionalPrincipal 、
WithOptionalDependent”
來用人和聯絡方式來舉例配置一對一關係
public class Person : BaseEntity { public string Name { get; set; } public virtual PersonContact PersonContact { get; set; } }View Code
public class PersonContact : BaseEntity { public string Email { get; set; } public virtual Person Person { get; set; } }View Code
public class PersonMap:EntityTypeConfiguration<Person> { public PersonMap() { ToTable("tb_Person"); HasKey(x => x.Id); HasOptional(x => x.PersonContact) .WithOptionalPrincipal(x => x.Person).Map(x => x.MapKey("FK_Person_Id")); } }View Code
public class PersonContactMap:EntityTypeConfiguration<PersonContact> { public PersonContactMap() { ToTable("tb_PersonContact"); HasKey(x => x.Id); } }View Code
表結構如下
上面我使用的是HasOptional、WithOptionalPrincipal來配置的,外來鍵建立在PersonContact表上
principal主要的,dependent從屬的 那使用dependent 外來鍵就會被建立在Person表上
當然還有其他的配置,這一點我就不說了,免得亂了,各位可以自己去研究。