1. 程式人生 > >EF6學習筆記四:一對多、多對多、一對一關係配置(Fluent API)

EF6學習筆記四:一對多、多對多、一對一關係配置(Fluent API)

 

要專業系統地學習EF前往《你必須掌握的Entity Framework 6.x與Core 2.0》這本書的作者(汪鵬,Jeffcky)的部落格:https://www.cnblogs.com/CreateMyself/    

現在就來到了重中之重的配置了:一對多、多對多、一對一關係的配置,我這裡全部使用Fulent API 的方式來

一對多

一對多最簡單了,寫好你的資料模型,什麼配置都不用做,生成的表結構就是你想要的

來個簡單基類

public class BaseEntity
    {
        public BaseEntity() {
            
this.Id = Guid.NewGuid().ToString(); this.AddTime = DateTime.Now; } public string Id { get; set; } public DateTime AddTime { get; set; } }
View Code

 

來個簡單圖書館類

public class Library:BaseEntity
    {
        public string Name { get; set
; } public virtual ICollection<Book> Books { get; set; } }
View Code

 

來個簡單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表上

當然還有其他的配置,這一點我就不說了,免得亂了,各位可以自己去研究。