假設(shè)有以下兩個(gè)實(shí)體:
public class Student { public int StuID { get; set; } public string? Name { get; set; } public IEnumerable<Homework>? Homeworks { get; set; } } public class Homework { public string? Class { get; set; } public string? Subject { get; set; } }
Homework 類表示家庭作業(yè),它并不是獨(dú)立使用的,而是與學(xué)生類(Student)有依賴關(guān)系。一位學(xué)生有多個(gè)家庭作業(yè)記錄,即 Homework 對(duì)象用于記錄每位同學(xué)的作業(yè)的。按照這樣的前提,Student 是主對(duì)象,Homework 是從對(duì)象。
Student 對(duì)象有個(gè) Homeworks 屬性,用于引用 Homework 對(duì)象,也就是所謂的“導(dǎo)航屬性”。這個(gè)“導(dǎo)航”,估計(jì)意思就是你通過(guò)這個(gè)屬性可以找到被引用的另一個(gè)實(shí)體對(duì)象,所以稱之為導(dǎo)航,就是從 Navigation 的翻譯。
隨后,咱們要從 DbContext 類派生出自定義的數(shù)據(jù)庫(kù)上下文。
public class MyDbContext : DbContext { // 映射的數(shù)據(jù)表,名稱默認(rèn)與屬性名稱一樣 // 即 Students + Works public DbSet<Student> Students => Set<Student>(); public DbSet<Homework> Works => Set<Homework>(); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // 設(shè)置連接字符串 optionsBuilder.UseSqlServer(Helper.Conn_STRING); } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 設(shè)置主鍵 modelBuilder.Entity<Student>().HasKey(s => s.StuID); // 建立主從關(guān)系 modelBuilder.Entity<Student>().OwnsMany(s => s.Homeworks); } }
連接字符串是老周事先配置好的,連的是 SQL Server。
public class Helper { public const string Conn_STRING = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=stuDB;Integrated Security=True"; }
用的是 LocalDB,這玩意兒方便。
其實(shí)這是一個(gè)控制臺(tái)應(yīng)用程序,并添加了 Nuget 包。
<ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.8" /> </ItemGroup>
好,回到咱們的代碼中,MyDbContext 重寫(xiě)了兩個(gè)方法:
1、重寫(xiě)?OnConfiguring 方法,做一些與該 Context 有關(guān)的配置,通常是配置連接字符串;也可能配置一下日志輸出。上面代碼中使用的是擴(kuò)展方法?UseSqlServer。這就是引用?Microsoft.EntityFrameworkCore.SqlServer Nuget 包的作用。
2、重寫(xiě)?OnModelCreating 方法。這個(gè)是設(shè)置實(shí)體類相關(guān)的模型屬性,以及與數(shù)據(jù)表的映射,或配置實(shí)體之間的關(guān)系。上述代碼中,老周做了兩件事:A、為 Student 實(shí)體設(shè)置主鍵,作為主鍵的屬性是 StuID;B、建立 Student 和 Homework 對(duì)象的主從關(guān)系,調(diào)用 OwnsMany 方法的意思是:一條 Student 記錄對(duì)應(yīng) N 條 Homework 記錄。因?yàn)?Student 類的 Homeworks 屬性是集合。
在 Main 方法中,咱們要做兩件事:A、根據(jù)上面的建模創(chuàng)建數(shù)據(jù)庫(kù);B、往數(shù)據(jù)庫(kù)中存一點(diǎn)數(shù)據(jù)。
static void Main(string[] args) { using (var ctx = new MyDbContext()) { //ctx.Database.EnsureDeleted(); bool res = ctx.Database.EnsureCreated(); if (res) { Console.WriteLine("已創(chuàng)建數(shù)據(jù)庫(kù)"); } } using(MyDbContext ctx = new()) { // 加點(diǎn)料 ctx.Students.Add(new Student { Name = "小張", Homeworks = new List<Homework> { new Homework{ Class = "數(shù)學(xué)", Subject = "3000道口算題"}, new Homework{ Class = "英語(yǔ)", Subject = "背9999個(gè)單詞"} } }); ctx.Students.Add(new Student { Name = "小雪", Homeworks = new Homework[] { new Homework{ Class = "歷史", Subject = "臨一幅《清明上河圖》"}, new Homework{ Class = "語(yǔ)文", Subject = "作文題:《百鬼日行》"} } }); // 保存 int x = ctx.SaveChanges(); Console.WriteLine("共保存了{(lán)0}條記錄", x); } }
EnsureCreated 方法會(huì)自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)。如果不存在數(shù)據(jù)庫(kù)且創(chuàng)建成功,返回 true,否則是 false。數(shù)據(jù)庫(kù)的名稱在連接字符串中配置過(guò)。
Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=stuDB;Integrated Security=True
接下來(lái),我們運(yùn)行一下。稍等幾秒鐘,看到控制臺(tái)輸出下面文本就算成功了。
已創(chuàng)建數(shù)據(jù)庫(kù)
共保存了6條記錄
然后,連上去看看有沒(méi)有數(shù)據(jù)庫(kù)。
看看,這表的名稱是不是和 MyDbContext 的兩個(gè)屬性一樣??
public class MyDbContext : DbContext { public DbSet<Student> Students => Set<Student>(); public DbSet<Homework> Works => Set<Homework>(); ……
你要是不喜歡用這倆名字,也可以發(fā)動(dòng)傳統(tǒng)技能(指老 EF),用 Table 特性給它們另取高名。
[Table("tb_students", Schema = "dbo")] public class Student { …… } [Table("tb_homeworks", Schema = "dbo")] public class Homework { …… }
刪除數(shù)據(jù)庫(kù),再運(yùn)行一次程序,然后再登錄數(shù)據(jù)庫(kù)看看,表名變了嗎?
那有伙伴們會(huì)問(wèn):有沒(méi)有現(xiàn)代技能?有的,使用 ToTable 方法定義映射的數(shù)據(jù)表名稱。
先去掉 Student、Homework 類上的 Table 特性,然后直接在重寫(xiě)?OnModelCreating 方法時(shí)配置。
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().ToTable("dt_students").HasKey(s => s.StuID); modelBuilder.Entity<Homework>().ToTable("dt_works"); // 建立主從關(guān)系 modelBuilder.Entity<Student>().OwnsMany(s => s.Homeworks); }
但是這樣寫(xiě)會(huì)報(bào)錯(cuò)的。因?yàn)?Homework 實(shí)體是 Student 的從屬對(duì)象,單獨(dú)調(diào)用 ToTable 方法在配置的時(shí)候會(huì)將其設(shè)置為獨(dú)立對(duì)象,而非從屬對(duì)象。
所以,正確的做法是在兩個(gè)實(shí)體建立了從屬性關(guān)系后再調(diào)用 ToTable 方法(Student 對(duì)象是主對(duì)象,它可以單獨(dú)調(diào)用)。
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().HasKey(s => s.StuID); modelBuilder.Entity<Student>() .ToTable("tb_students") .OwnsMany(s => s.Homeworks) .ToTable("tb_works"); }
因?yàn)?Homework 是 Student 的從屬,tb_works 表中要存在一個(gè)外鍵——引用 Student.StuID,這樣兩個(gè)表才能建立主從關(guān)系。如果單獨(dú)調(diào)用 Entity<Homework>.ToTable 映射表的話,那么表中不會(huì)添加引用 StuID 的外鍵列。就是默認(rèn)被配置為非主從模式。沒(méi)有了外鍵,tb_works 表中存的數(shù)據(jù)就無(wú)法知道是哪位學(xué)生的作業(yè)了。
這樣創(chuàng)建數(shù)據(jù)庫(kù)后,tb_works 表中就存在名為 StudentStuID 的列,它就是引用 Student.StuID 的外鍵。
CREATE TABLE [dbo].[tb_works] ( [StudentStuID] INT NOT NULL, [Id] INT IDENTITY (1, 1) NOT NULL, [Class] NVARCHAR (MAX) NULL, [Subject] NVARCHAR (MAX) NULL, CONSTRAINT [PK_tb_works] PRIMARY KEY CLUSTERED ([StudentStuID] ASC, [Id] ASC), CONSTRAINT [FK_tb_works_tb_students_StudentStuID] FOREIGN KEY ([StudentStuID]) REFERENCES [dbo].[tb_students] ([StuID]) ON DELETE CASCADE );
當(dāng)然,這個(gè)外鍵名字是根據(jù)實(shí)體類名(Student)和它的主鍵屬性名(StuID)生成的,如果你想自己搞個(gè)名字,也是可以的。
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().HasKey(s => s.StuID); modelBuilder.Entity<Student>() .ToTable("tb_students") .OwnsMany(s => s.Homeworks, tb => { tb.ToTable("tb_works"); tb.WithOwner().HasForeignKey("student_id"); }); }
這樣 tb_works 表中就有了名為 student_id 的外鍵。
CREATE TABLE [dbo].[tb_works] ( [student_id] INT NOT NULL, [Id] INT IDENTITY (1, 1) NOT NULL, [Class] NVARCHAR (MAX) NULL, [Subject] NVARCHAR (MAX) NULL, CONSTRAINT [PK_tb_works] PRIMARY KEY CLUSTERED ([student_id] ASC, [Id] ASC), CONSTRAINT [FK_tb_works_tb_students_student_id] FOREIGN KEY ([student_id]) REFERENCES [dbo].[tb_students] ([StuID]) ON DELETE CASCADE );
OwnsXXX 方法是指:俺是主表,我要“關(guān)照”一下從表;
WithOwner 方法是指:俺是從表,我要配置一下和主表之間建立聯(lián)系的參數(shù)(如上面給外鍵另起個(gè)名字)。
那么,我想把兩個(gè)表的列全自定義命名,可以嗎?當(dāng)然可以的。
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().HasKey(s => s.StuID); modelBuilder.Entity<Student>() .ToTable("tb_students", tb => { tb.Property(s => s.StuID).HasColumnName("sID"); tb.Property(s => s.Name).HasColumnName("stu_name"); }) .OwnsMany(s => s.Homeworks, tb => { tb.ToTable("tb_works"); tb.WithOwner().HasForeignKey("student_id"); tb.Property(w => w.Class).HasColumnName("wk_class"); tb.Property(w => w.Subject).HasColumnName("wk_sub"); }); }
兩個(gè)表的字段名都變了。
CREATE TABLE [dbo].[tb_students] ( [sID] INT IDENTITY (1, 1) NOT NULL, [stu_name] NVARCHAR (MAX) NULL, CONSTRAINT [PK_tb_students] PRIMARY KEY CLUSTERED ([sID] ASC) ); CREATE TABLE [dbo].[tb_works] ( [student_id] INT NOT NULL, [Id] INT IDENTITY (1, 1) NOT NULL, [wk_class] NVARCHAR (MAX) NULL, [wk_sub] NVARCHAR (MAX) NULL, CONSTRAINT [PK_tb_works] PRIMARY KEY CLUSTERED ([student_id] ASC, [Id] ASC), CONSTRAINT [FK_tb_works_tb_students_student_id] FOREIGN KEY ([student_id]) REFERENCES [dbo].[tb_students] ([sID]) ON DELETE CASCADE );
注意:Homework 類中沒(méi)有定義 Id 屬性(主鍵),它是自動(dòng)生成的。
?
有大伙伴會(huì)想,在?OnModelCreating 方法中建模我頭有點(diǎn)暈,我能不能在定義實(shí)體類的時(shí)候,直接通過(guò)特性批注來(lái)實(shí)現(xiàn)主從關(guān)系呢?那肯定可以的了。
[Table("tb_students")] [PrimaryKey(nameof(StuID))] public class Student { [Column("sID")] public int StuID { get; set; } [Column("st_name")] public string? Name { get; set; } // 這是導(dǎo)航屬性,不需要映射到數(shù)據(jù)表 public IEnumerable<Homework>? Homeworks { get; set; } } [Owned] [Table("tb_homeworks")] [PrimaryKey(nameof(wID))] public class Homework { [Column("wk_id")] public int wID { get; set; } [Column("wk_class")] public string? Class { get; set; } [Column("wk_sub")] public string? Subject { get; set; } [ForeignKey("student_id")] //設(shè)置外鍵名稱 public Student? StudentObj { get; set; } }
PrimaryKey 特性設(shè)置實(shí)體類中哪些屬性為主鍵,使用屬性成員的名稱,而不是數(shù)據(jù)表字段名稱。
在 Homework 類上用到?Owned 特性,表示其他對(duì)象如果引用了 Homework,就會(huì)自動(dòng)建立主從關(guān)系—— Homework 為從屬對(duì)象。
ForeignKey 特性指定外鍵的名稱。雖然?StudentObj 屬性的類型是 Student 類,但在建立數(shù)據(jù)表時(shí),只引用了 Student 類的 StuID 屬性。
此時(shí),可以清空?OnModelCreating 方法中的代碼了。
生成的數(shù)據(jù)表結(jié)構(gòu)與上文差不多。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-514743.html
CREATE TABLE [dbo].[tb_students] ( [sID] INT IDENTITY (1, 1) NOT NULL, [st_name] NVARCHAR (MAX) NULL, CONSTRAINT [PK_tb_students] PRIMARY KEY CLUSTERED ([sID] ASC) ); CREATE TABLE [dbo].[tb_homeworks] ( [wk_id] INT IDENTITY (1, 1) NOT NULL, [wk_class] NVARCHAR (MAX) NULL, [wk_sub] NVARCHAR (MAX) NULL, [student_id] INT NULL, CONSTRAINT [PK_tb_homeworks] PRIMARY KEY CLUSTERED ([wk_id] ASC), CONSTRAINT [FK_tb_homeworks_tb_students_student_id] FOREIGN KEY ([student_id]) REFERENCES [dbo].[tb_students] ([sID]) );
當(dāng)然了,最好的做法是將特性批注與?OnModelCreating? 方法結(jié)合使用。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-514743.html
到了這里,關(guān)于【EF Core】實(shí)體的主、從關(guān)系的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!