一直以來對EF和EF Core都是單獨分開來寫,從未以比較的形式來說解,若是您既用過EF 6.x也用過用EF Core是否有了解過EF和EF Core在插入數據時和返回主鍵有何異同呢?本篇博客是坐在電腦旁本打算寫寫EF 6.x插入數據注意的問題,心想何不比較兩者呢?我也是在探索中(邊敲代碼邊寫博客中),下面咱們來看看。數據庫
using (var ctx = new EfDbContext()) { ctx.Database.Log = Console.WriteLine; var customer = new Customer() { Email = "2752154844@qq.com", Name = "Jeffcky", Orders = new List<Order>() { new Order() { Code = "1", CreatedTime = DateTime.Now, ModifiedTime = DateTime.Now, Quantity = 10, Price =100 } } }; }
上述Customer和Order爲一對多關係,Order實體中有Customer實體的外鍵,上述咱們同時給Customer和Order賦了值,因此當咱們插入Customer的同時Order表中也插入了數據,此時Order的CustomerId是Customer的主鍵,咱們根本不須要爲Order中的CustomerId顯式賦值,這一點毋庸置疑。我想說的是若是兩個表沒有很強的關聯關係,怎麼說呢,換言之兩個表沒有配置所謂的關係又或許咱們沒有配置關係,一個表中列須要用到另一個表的主鍵,那麼這種的狀況下,咱們會以怎樣的方式插入數據呢?實踐是檢驗真理的惟一標準,下面咱們來試試。ide
public class TestA { public int Id { get; set; } public string Other { get; set; } } public class TestB { public int Id { get; set; } public int TestAId { get; set; } public string Other { get; set; } }
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<TestA>() .ToTable("TestAs") .HasKey(p => p.Id) .Property(p => p.Other); modelBuilder.Entity<TestB>() .ToTable("TestBs") .HasKey(p => p.Id) .Property(p => p.Other); }
上述咱們給出TestA和TestB,TestA和TestB沒有任何關係,可是咱們在插入TestB數據時須要獲得Test的主鍵,那咱們下面就進行以下數據添加。測試
using (var ctx = new EfDbContext()) { ctx.Database.Log = Console.WriteLine; var testA = new TestA() { Other = "other" }; ctx.TestAs.Add(testA); ctx.SaveChanges(); var testB = new TestB() { Other = "other", TestAId = testA.Id }; ctx.TestBs.Add(testB); ctx.SaveChanges(); }
此時咱們看到提交後數據最終可以保存到數據庫中,反觀上述提交代碼,咱們首先是提交了TestA保存到數據庫後而後拿到TestA的主鍵,而後再是提交TestB並保存到數據庫中。那咱們有沒有考慮是否直接一次性提交呢,註釋TestA提交,以下:ui
using (var ctx = new EfDbContext()) { ctx.Database.Log = Console.WriteLine; var testA = new TestA() { Other = "other" }; ctx.TestAs.Add(testA); //ctx.SaveChanges(); var testB = new TestB() { Other = "other", TestAId = testA.Id }; ctx.TestBs.Add(testB); ctx.SaveChanges(); }
WOW不行啊,下面咱們來看看在EF Core中實現是否是能夠,試試就知道了,別猜想。spa
modelBuilder.Entity<TestA>(e => { e.ToTable("TestAs"); e.HasKey(p => p.Id); e.Property(p => p.Other); }); modelBuilder.Entity<TestB>(e => { e.ToTable("TestBs"); e.HasKey(p => p.Id); e.Property(p => p.Other); });
using (var context = new EFCoreDbContext()) { var testA = new TestA() { Other = "other" }; context.TestAs.Add(testA); //ctx.SaveChanges(); var testB = new TestB() { Other = "other", TestAId = testA.Id }; context.TestBs.Add(testB); context.SaveChanges(); }
若是分兩次提交那麼不管是在EF 6.x仍是EF Core中都是同樣沒有任何不一樣(在EF Core中沒有測試也不用測試)。若是是一次性提交,此時在EF 6.x中的TestB中的TestAId爲插入的是0,而EF Core中的TestB中的TestAId爲-2147482647即INT類型最小值,至少找到了不一樣所在。Jeff自問自答的模式要來了,是否是就這樣結束了呢?上述咱們對TestA和TestB兩個實體未配置任何關係,咱們通過測試證實一次性提交併未達到咱們預期,要是咱們依然在不配置關係的前提下給出導航屬性而後一次性提交呢,以下:code
public class TestA { public int Id { get; set; } public string Other { get; set; } } public class TestB { public int Id { get; set; } public int TestAId { get; set; } public string Other { get; set; } public TestA TestA { get; set; } }
接下來咱們在EF Core控制檯再次運行上述代碼看看,您思考下會不會將TestA中的主鍵添加進去呢。blog
若是您在EF 6.x中一樣添加上述導航屬性也是好使的,我就不測試了,那到此咱們得出結論:若兩個實體未顯式配置任何關係但一個表須要獲得另一個表的主鍵,不管是在EF 6.x仍是在EF Core中進行一次性提交很差使,只是在EF 6.x中一個表須要獲得另一個表的主鍵爲0,而在EF Core中倒是INT類型最小值,若咱們顯式配置了導航屬性,那麼不管是在EF 6.x仍是EF Core中一次性提交可達到咱們預期。 作用域
若是是主鍵爲INT類型,默認狀況不管是EF 6.x仍是EF Core都將自動映射配置爲自增加,要是咱們顯式配置了主鍵,那麼對於EF 6.x和EF Core會有何不一樣呢?咱們首先看看EF 6.x,以下(咱們清除以前已提交數據):get
using (var ctx = new EfDbContext()) { ctx.Database.Log = Console.WriteLine; var testA = new TestA() { Id = 1, Other = "other" }; ctx.TestAs.Add(testA); ctx.SaveChanges(); }
從上述咱們提交三次看出,壓根不叼咱們設置的值,那麼咱們看看生成的SQL語句是怎樣的呢,以下圖:博客
這下咱們明白了此時經過scope_identity返回爲當前會話和當前做用域中的TestA表生成的最新標識值。接下來咱們來看看EF Core。
using (var context = new EFCoreDbContext()) { var testA = new TestA() { Id = 1, Other = "other" }; context.TestAs.Add(testA); context.SaveChanges(); }
當咱們進行第二次提交後將拋出異常,以下:
咱們一樣看看在EF Core中生成的SQL是怎樣的。
原來如此沒有返回當前會話自增加值,同時咱們知道不能顯式插入值,那就是關閉了IDENTITY_Insert。因此咱們得出結論:在以INT做爲主鍵且自增加時,在EF 6.x中可以顯式給出值,而在EF Core中不能顯式給定值即關閉了IDENTITY_INSERT不能顯式插入主鍵值。
本節咱們詳細敘述了EF 6.x和EF Core中插入數據和返回主鍵的不一樣之處,雖然做用不大,能夠做爲了解,最近比較累,可能會停更一小段時間,好好休息一下,書出版了會及時告知同行關注者。