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既談不上精通更談不上不專家只不過是平時私下喜歡研究罷了,書中大部分都是我我的的理解,同時技術更新迭代太快,我也一直在追逐中而非停滯不前,我相信:不管出身環境怎樣,自身天賦如何,篤定均可以經過自身的努力來改變而且成長

相關文章
相關標籤/搜索