本文來自和何鎮汐大哥的探討,不少時候我習慣於和別人交流事後會思考一些問題,不管是天馬行空仍是淺薄的想法都會記錄下來,或許看到此博文的您能給我更多的思考,與人交流總能收穫不少東西,出發點不同則結論 不同,思惟方式不同則路徑不同,願你我共同進步。html
首先依然給出本文須要用到的兩個實體,以下:app
public class Blog { public int Id { get; set; } public string Name { get; set; } public string Url { get; set; } public DateTime CreatedTime { get; set; } public DateTime ModifiedTime { get; set; } public byte Status { get; set; } public bool IsDeleted { get; set; } public ICollection<Post> Posts { get; set; } = new List<Post>(); }
public class Post { public int Id { get; set; } public string Name { get; set; } public DateTime CreatedTime { get; set; } public DateTime ModifiedTime { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } }
在EF Core中給咱們提供了Update和UpdateRange方法,這兩個方法你說做用大吧,我看做用也沒有那麼大。要利用這兩個方法,必須對值進行一一賦值,以下:post
var dbContext = new EFCoreDbContext(); var dbBlog = dbContext.Blogs.Include(d => d.Posts).FirstOrDefault(d => d.Id == 1); dbBlog.Name = "Jeffcky"; foreach (var post in dbBlog.Posts) { post.Name = "《你必須掌握的EntityFramework 6.x與Core 2.0》"; } dbContext.Update(dbBlog); dbContext.SaveChanges();
在EF 6.x中缺失Update和UpdateRange方法,可是它能夠進行以下更新啊不是。性能
var dbContext = new EFCoreDbContext(); var newBlog = new Blog() { Id = 1, Name = "Jeffcky1", IsDeleted = false, Status = 0, Url = "https://www.cnblogs.com/CreateMyself", CreatedTime = DateTime.Now, ModifiedTime = DateTime.Now, Posts = new List<Post>() { new Post() { Id = 1, BlogId = 1, Name = "EF Core TrackGraph", CreatedTime = DateTime.Now, ModifiedTime = DateTime.Now } } }; var dbBlog = dbContext.Blogs.Include(d => d.Posts).FirstOrDefault(d => d.Id == 1); dbContext.Entry(dbBlog).CurrentValues.SetValues(newBlog); dbContext.SaveChanges();
不太瞭解詳情的童鞋可能就說了在EF Core中也能夠利用上述CurrentValues來指定更新列啊,若是您這樣想那就大錯特錯了,來咱們在EF Core中一樣運行上述代碼經過對比先後表中數據看看。測試
更新前spa
更新後code
咱們經過對比可看到,導航屬性對應的表沒有進行更新,不要問我爲啥,在前面我也有講過在EF Core中這種狀況相似於和添加同樣經過手動這是狀態爲Added,在EF 6.x中只要更新主表則對應與之相關的導航屬性也會更新,可是在EF Core中只會更新主表,EF 6.x這麼好的指定更新反而被剔除了,實在不該該啊。有人說賦值兩次啊,很差意思也不行,以下:htm
var dbContext = new EFCoreDbContext(); var newBlog = new Blog() { Id = 1, Name = "Jeffcky1", IsDeleted = false, Status = 0, Url = "https://www.cnblogs.com/CreateMyself", CreatedTime = DateTime.Now, ModifiedTime = DateTime.Now, Posts = new List<Post>() { new Post() { Id = 1, BlogId = 1, Name = "EF Core TrackGraph", CreatedTime = DateTime.Now, ModifiedTime = DateTime.Now } } }; var dbBlog = dbContext.Blogs.Include(d => d.Posts).FirstOrDefault(d => d.Id == 1); dbContext.Entry(dbBlog).CurrentValues.SetValues(newBlog); dbContext.Entry(dbBlog.Posts).CurrentValues.SetValues(newBlog.Posts); dbContext.SaveChanges();
上述這種方式對關係映射是不行的,可是如果複雜屬性則是能夠,以下:blog
[Owned] public class StreetAddress { public string Street { get; set; } public string City { get; set; } } public class Order { public int Id { get; set; } public StreetAddress ShippingAddress { get; set; } }
var dbContext = new EFCoreDbContext(); var order = dbContext.Orders.FirstOrDefault(); order.ShippingAddress.City = "city"; order.ShippingAddress.Street = "street"; dbContext.SaveChanges();
這樣更新確定是能夠的,咱們不作過多探討,利用CurrentValues只能進行兩次賦值才行,以下。ip
var newOrder = new Order() { Id = 1, ShippingAddress = new StreetAddress() { City = "city", Street = "street" } }; var dbContext = new EFCoreDbContext(); var order = dbContext.Orders.FirstOrDefault(); dbContext.Entry(order).CurrentValues.SetValues(newOrder); dbContext.Entry(order.ShippingAddress).CurrentValues.SetValues(newOrder.ShippingAddress); var result = dbContext.SaveChanges();
讓咱們再次回到更新Blog,除了利用CurrentValues指定更新外,咱們還能夠在查詢Posts時不進行顯式加載,而後調用直接將更新newBlog賦值與dbBlog,這種方式和手動賦值本質同樣,可是至少不用一一賦值不是,以下:
var dbContext = new EFCoreDbContext(); var newBlog = new Blog() { Id = 1, Name = "Jeffcky1", IsDeleted = false, Status = 0, Url = "https://www.cnblogs.com/CreateMyself", CreatedTime = DateTime.Now, ModifiedTime = DateTime.Now, Posts = new List<Post>() { new Post() { Id = 1, BlogId = 1, Name = "EF Core TrackGraph", CreatedTime = DateTime.Now, ModifiedTime = DateTime.Now } } }; var dbBlog = dbContext.Blogs .AsNoTracking() .Include(d => d.Posts).FirstOrDefault(d => d.Id == 1); dbBlog = newBlog; dbContext.Update(dbBlog); var result = dbContext.SaveChanges();
說了這麼多在EF Core中對於指定更新列不是太友好,當屬性過多利用手動賦值就太麻煩,應該保留EF 6.x中利用CurrntValues對導航屬性也進行直接更新豈不更好,若是調用Update方法將當前實體與快照中的實體比較指定更新列應該纔是最佳方案。
咱們看以下三個示例實體
public class A { public int Id { get; set; } public ICollection<B> Bs { get; set; } } public class B { public int Id { get; set; } public C C { get; set; } } public class C { public int Id { get; set; } }
此時咱們來查詢A並經過顯式加載B和C,以下:
var dbContext = new EFCoreDbContext(); var As = dbContext.As.Include(d => d.Bs).ThenInclude(d => d.C).ToList();
大部分查詢咱們都會進行如上查詢,可是咱們是否思考是上述是否爲最佳方案呢?或者性能更好呢?我也不知道,我也只是純屬猜想,由於要是咱們進行以下加載數據呢?
static void IncludeLoadCollection(EFCoreDbContext dbContext, object obj) { var entityEntry = dbContext.Entry(obj); foreach (var collection in entityEntry.Collections) { if (collection.IsLoaded) { continue; } collection.Load(); if (collection.CurrentValue != null) { foreach (var child in collection.CurrentValue) { IncludeLoadCollection(dbContext, child); } } } }
var dbContext = new EFCoreDbContext(); var a = dbContext.As.FirstOrDefault(); IncludeLoadCollection(dbContext, a);
如上代碼未經測試,只是做爲我的思考而給,您看到後私下可自行測試對比上述方案和經過Include....ThenInclude哪一種方案更好呢?本文稍微講解了下我的認爲EF Core對於指定更新沒有一個恰當的方式除了手動更新列外,固然字段太多,大部分狀況下都會藉助AutoMapper等進行DTO。
現京東和淘寶上可正式預售購買《你必須掌握的EntityFramework 6.x與Core 2.0》書籍,我博客右上方也給了一個購買連接,讓各位久等了。感謝各位同行一直以來的大力支持,同時也再次感謝博客園這個大平臺,給了我機會去分享技術,我對EF既談不上精通更談不上不專家只不過是平時私下喜歡研究罷了,書中大部分都是我我的的理解,同時技術更新迭代太快,我也一直在追逐中而非停滯不前,我相信:不管出身環境怎樣,自身天賦如何,篤定均可以經過自身的努力來改變而且成長。