EntityFramework Core指定更新导航属性了解一下?

前言

本文来自和何镇汐大哥的探讨,不少时候我习惯于和别人交流事后会思考一些问题,不管是天马行空仍是浅薄的想法都会记录下来,或许看到此博文的您能给我更多的思考,与人交流总能收获不少东西,出发点不同则结论 不同,思惟方式不同则路径不同,愿你我共同进步。html

EntityFramework Core无跟踪视图

首先依然给出本文须要用到的两个实体,以下: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方法将当前实体与快照中的实体比较指定更新列应该才是最佳方案。

Include....ThenInclude加载导航属性是否为最佳方案呢? 

咱们看以下三个示例实体

    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既谈不上精通更谈不上不专家只不过是平时私下喜欢研究罢了,书中大部分都是我我的的理解,同时技术更新迭代太快,我也一直在追逐中而非停滞不前,我相信:不管出身环境怎样,自身天赋如何,笃定均可以经过自身的努力来改变而且成长

相关文章
相关标签/搜索