本文来自和何镇汐大哥的探讨,不少时候我习惯于和别人交流事后会思考一些问题,不管是天马行空仍是浅薄的想法都会记录下来,或许看到此博文的您能给我更多的思考,与人交流总能收获不少东西,出发点不同则结论 不同,思惟方式不同则路径不同,愿你我共同进步。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既谈不上精通更谈不上不专家只不过是平时私下喜欢研究罢了,书中大部分都是我我的的理解,同时技术更新迭代太快,我也一直在追逐中而非停滞不前,我相信:不管出身环境怎样,自身天赋如何,笃定均可以经过自身的努力来改变而且成长。