EntityFrameworkCore1.1Add、Attach、Update、Remove方法如何高效使用詳解

EntityFramework Core 1.1方法理論詳解

當(dāng)我們利用EF Core查詢數(shù)據(jù)庫時(shí)如果我們不顯式關(guān)閉變更追蹤的話,此時(shí)實(shí)體是被追蹤的,關(guān)于變更追蹤我們下節(jié)再敘。就像我們之前在EF 6.x中討論的那樣,不建議手動(dòng)關(guān)閉變更追蹤,對(duì)于有些特殊情況下,關(guān)閉變更追蹤可能會(huì)導(dǎo)致許多問題的發(fā)生。

創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供連云港網(wǎng)站建設(shè)、連云港做網(wǎng)站、連云港網(wǎng)站設(shè)計(jì)、連云港網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、連云港企業(yè)網(wǎng)站模板建站服務(wù),10多年連云港做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

實(shí)體狀態(tài)

對(duì)于EF Core 1.1中依然有四種狀態(tài),有的人說不是有五種狀態(tài)么,UnChanged、Added、Modified、Deleted、Detached。如果我們按照變更追蹤來劃分的話,實(shí)際上只有四種,將Detached排除在外,Detached不會(huì)被上下文所追蹤。那么狀態(tài)如何改變的呢??jī)?nèi)部有一個(gè)IStateManager接口,通過此接口來對(duì)實(shí)體狀態(tài)進(jìn)行管理,此時(shí)再取決于SaveChanges被調(diào)用后背后是如何進(jìn)行處理,我也就稍微看了下源碼,深入的東西沒去過多研究。

Added:實(shí)體還未插入到數(shù)據(jù)庫當(dāng)中,當(dāng)調(diào)用SaveChanges后將修改其狀態(tài)并將實(shí)體插入到數(shù)據(jù)庫。

UnChanged:實(shí)體存在數(shù)據(jù)庫中,但是在客戶端未進(jìn)行修改,當(dāng)調(diào)用SaveChanges后將忽略。

Modified:實(shí)體存在數(shù)據(jù)庫中,同時(shí)實(shí)體在客戶端也進(jìn)行了修改,當(dāng)調(diào)用SaveChanges后將更改其狀態(tài)并更新數(shù)據(jù)持久化到數(shù)據(jù)庫。

Deleted:實(shí)體存在數(shù)據(jù)庫中,當(dāng)調(diào)用SaveChanges方法后將刪除實(shí)體。

實(shí)體方法

在EF Core 1.1中依然存在Add、Attach、Update方法,我們通過上下文或者DbSet<TEntity>能夠看到,當(dāng)將實(shí)體傳遞到這些方法中時(shí),它們與實(shí)體追蹤可達(dá)圖緊密聯(lián)系在一起,比如說我們之前討論的博客的導(dǎo)航屬性文章的發(fā)表,當(dāng)我們添加文章的發(fā)表的這個(gè)實(shí)體時(shí),然后調(diào)用Add方法后此時(shí)文章的發(fā)表這個(gè)實(shí)體也就被添加。在EF 6.x中我們說過當(dāng)我們調(diào)用Add等方法時(shí)EF內(nèi)部機(jī)制將會(huì)自動(dòng)調(diào)用DetectChanges,但是在EF Core 1.1中則不再調(diào)用DetectChanges方法??湛跓o憑,我下載了源碼,如下:

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public virtual void Add(TEntity item)
        {
            var entry = _stateManager.GetOrCreateEntry(item);            if (entry.EntityState == EntityState.Deleted                || entry.EntityState == EntityState.Detached)
            {
                OnCountPropertyChanging();

                entry.SetEntityState(EntityState.Added);

                _count++;

                OnCollectionChanged(NotifyCollectionChangedAction.Add, item);

                OnCountPropertyChanged();
            }
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

上述我們沒有看到任何自動(dòng)調(diào)用DetectChanges的邏輯,在EF 6.x中我們講到當(dāng)調(diào)用SaveChanges時(shí)此時(shí)會(huì)回調(diào)DetectChanges,而在EF Core 1.1中同樣也是如此,所以相對(duì)于EF 6.x而言,EF Core 1.1只是在SaveChanges時(shí)回調(diào)DetectChanges,在Add、Attacth、Update等方法則不再回調(diào)DetectChanges,這樣的話性能就會(huì)好很多。我們看到源代碼中調(diào)用SaveChanges時(shí)邏輯如下:

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public virtual int SaveChanges(bool acceptAllChangesOnSuccess)
        {
            CheckDisposed();            TryDetectChanges();            try
            {                return StateManager.SaveChanges(acceptAllChangesOnSuccess);
            }            catch (Exception exception)
            {..}
         }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

接下來我們?cè)賮砜纯串?dāng)調(diào)用Add、Update等方法時(shí)到底發(fā)生了什么。

Add:當(dāng)調(diào)用Add方法時(shí)就沒什么可說的了,此時(shí)將在圖中的對(duì)應(yīng)的所有實(shí)體推入到Added狀態(tài),也就說在調(diào)用SaveChanges時(shí)將會(huì)插入到數(shù)據(jù)庫中去。

Attach:當(dāng)調(diào)用Attach方法時(shí)將在圖中的所有實(shí)體推入到UnChanged狀態(tài),但是有一個(gè)額外情況,比如我們?cè)谝粋€(gè)類中添加導(dǎo)航屬性數(shù)據(jù)時(shí),此時(shí)Attach的話將會(huì)使用混合模式,將此實(shí)體的狀態(tài)為UnChanged而導(dǎo)航屬性的狀態(tài)則是Added狀態(tài),所以當(dāng)插入到數(shù)據(jù)庫中時(shí),這個(gè)已存在的數(shù)據(jù)將不會(huì)被保存,只有新添加的導(dǎo)航屬性數(shù)據(jù)才會(huì)被插入到數(shù)據(jù)庫中去。

Update:Update方法和Attach方法一樣只是將其狀態(tài)修改為Modified,而將新添加的實(shí)體的修改將進(jìn)行插入。

Remove:當(dāng)調(diào)用Remove方法時(shí)此時(shí)它只會(huì)影響傳遞給該方法的實(shí)體,不會(huì)去遍歷實(shí)體的可到達(dá)圖。如果一個(gè)實(shí)體的狀態(tài)是UnChanged或者M(jìn)odified,說明該實(shí)體已存在數(shù)據(jù)庫中,此時(shí)只需將其狀態(tài)修改為Deleted。如果實(shí)體的狀態(tài)為Added,此時(shí)說明該實(shí)體在數(shù)據(jù)庫中不存在,此時(shí)會(huì)脫離上下文而不被跟蹤。所以Remove方法側(cè)重強(qiáng)調(diào)實(shí)體要被追蹤,否則的話需要首先被Attach然后將其推入到Deleted狀態(tài)。

Range方法 

在EF Core 1.1中多了AddRanges、UpdateRanges等方法,它們和實(shí)際調(diào)用多次調(diào)用非Range方法其實(shí)是一樣的,它內(nèi)部也會(huì)去遍歷實(shí)體集合并更新其狀態(tài),如下:

public virtual void UpdateRange([NotNull] IEnumerable<object> entities)            => SetEntityStates(Check.NotNull(entities, nameof(entities)), EntityState.Modified);

我們?cè)倏碨etEntityStates這個(gè)方法的實(shí)現(xiàn)。

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        private void SetEntityStates(IEnumerable<object> entities, EntityState entityState)
        {            var stateManager = StateManager;            foreach (var entity in entities)
            {
                SetEntityState(stateManager.GetOrCreateEntry(entity), entityState);
            }
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

EF Core內(nèi)部機(jī)制的處理肯定比我們之前手動(dòng)去遍歷添加實(shí)體集合性能要高,意外看到一篇文章上有說僅僅只高效一點(diǎn),因?yàn)镽ange方法自動(dòng)調(diào)用DetectChanges方法,找了半天也沒看見在哪里調(diào)用DetectChanges,郁悶,算是一點(diǎn)疑惑吧。

【注意】EF團(tuán)隊(duì)之前一直在承諾EF Core會(huì)更高效和更高可擴(kuò)展,但是我閱讀源碼發(fā)現(xiàn)內(nèi)部還是自動(dòng)調(diào)用了DetectChanges,性能方面的話還是不算太高效,但是,但是源碼中已經(jīng)明確給出,關(guān)于DetectChanges方法,未來對(duì)于這個(gè)api會(huì)進(jìn)行更改或者徹底移除,源碼注釋如下:

         /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used        ///     directly from your code. This API may change or be removed in future releases.        /// </summary>
        void DetectChanges([NotNull] IStateManager stateManager);

追蹤圖 

對(duì)于變更追蹤也好,默認(rèn)啟用變更追蹤也好,我們都是通過ChangeTracker屬性來獲取到,如下:

  EFCoreContext efCoreContext;
  efCoreContext.ChangeTracker.AutoDetectChangesEnabled;
  efCoreContext.ChangeTracker.DetectChanges;

在ChangeTracker中也有一個(gè)重要的方法那就是如下:

 efCoreContext.ChangeTracker.TrackGraph;

我們暫且起名為跟蹤圖吧,它是對(duì)實(shí)體狀態(tài)的完全控制,比如我們?cè)趯?shù)據(jù)插入到數(shù)據(jù)庫之前想設(shè)置其某一個(gè)值為臨時(shí)值,我們就可以通過該方法來實(shí)現(xiàn)。

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

            Blog blog;            using (var efCoreContext = new EFCoreContext(options))
            {
                efCoreContext.ChangeTracker.TrackGraph(blog, node =>
                {                    var entry = node.Entry;                    if ((int)entry.Property("Id").CurrentValue < 0)
                    {
                        entry.State = EntityState.Added;
                        entry.Property("Id").IsTemporary = true;
                    }                    else
                    {
                        entry.State = EntityState.Modified;
                    }
                });
            }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

在EF Core 1.1其余的就是關(guān)于Add、Update等方法的異步操作了,對(duì)于操作數(shù)據(jù)庫不至于阻塞的情況也還是挺好的。

EntityFramework Core 1.1方法實(shí)踐詳解

關(guān)于EF Core 1.1中一些基本的知識(shí)我們過了一遍,下面我們來看看這些方法到底該如何高效使用呢?

Add/AddRange

關(guān)于這個(gè)方法就沒有太多敘述的了,對(duì)應(yīng)的則是異步方法。我們重點(diǎn)看看其他的方法。

Update/UpdateRange

當(dāng)我們根據(jù)主鍵去更新所有實(shí)體這個(gè)so easy了,我們?cè)贐log表添加如下數(shù)據(jù)。

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

(1)更新方式一

現(xiàn)在我們查出Id=1的實(shí)體,然后將Name進(jìn)行修改如下:

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        IBlogRepository _blogRepository;        public HomeController(IBlogRepository blogRepository)
        {
            _blogRepository = blogRepository;
        }        public IActionResult Index()
        {            var blog = _blogRepository.GetSingle(d => d.Id == 1);
            blog.Name = "EntityFramework Core 1.1";
            _blogRepository.Commit();            return View();
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

上述我們直接查詢出來主鍵對(duì)應(yīng)的實(shí)體然后修改其值,最后提交更新其實(shí)體的對(duì)應(yīng)修改的屬性。最后順理成章的數(shù)據(jù)字段進(jìn)行了修改

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

我們知道因?yàn)椴樵兂鰜淼膶?shí)體在未關(guān)閉變更追蹤的情況下始終都是被追蹤的,所以必須進(jìn)行對(duì)應(yīng)修改,但是要是下面的情況呢。

        public IActionResult Index(int Id,Blog blog)
        {            return Ok();
        }

在客戶端對(duì)數(shù)據(jù)進(jìn)行了修改,我們需要根據(jù)主鍵Id進(jìn)行對(duì)應(yīng)屬性修改,當(dāng)然不希望多此一舉的話,我們可以根據(jù)主鍵Id去查詢對(duì)應(yīng)的實(shí)體,然后將屬性進(jìn)行賦值最后提交修改保存到數(shù)據(jù)庫中,大概就演變成如下情況。

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public IActionResult Index(int Id,Blog blog)
        {            var oldBlog = _blogRepository.GetSingle(d => d.Id == Id);
            oldBlog.Name = blog.Name;
            oldBlog.Url = blog.Url;
            _blogRepository.Commit();            return Ok();
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

誠(chéng)然上述方法能達(dá)到我的目的,其實(shí)還有簡(jiǎn)便的方法,如下:

(2)更新方式二

既然有簡(jiǎn)單的方法為何我們不用呢,這樣的場(chǎng)景就是更新指定屬性,以往的情況都是自己封裝一個(gè)Update方法,然后利用反射去包含需要修改的屬性接著更改其屬性的狀態(tài)為修改,最后提交修改即可。是的,這就是我們說的方法,但是,但是在EF Core 1.1中完全不需要我們?nèi)シ庋b,我們需要做的只是封裝成一個(gè)通用方法即可,內(nèi)置實(shí)現(xiàn)EF Core已經(jīng)幫我們實(shí)現(xiàn),我們來看看。

void Update(T entity, params Expression<Func<T, object>>[] properties);

很熟悉吧,我們?cè)诨鶄}儲(chǔ)接口給出這樣一個(gè)接口,接著我們來實(shí)現(xiàn)此接口,如下:

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public void Update(T entity, params Expression<Func<T, object>>[] properties)
        {
            _context.Entry(entity).State = EntityState.Unchanged;            foreach (var property in properties)
            {                var propertyName = ExpressionHelper.GetExpressionText(property);
                _context.Entry(entity).Property(propertyName).IsModified = true;
            }
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

是不是夠簡(jiǎn)單粗暴,開源就是好啊,查找資料時(shí)發(fā)現(xiàn)老外已經(jīng)給出了具體實(shí)現(xiàn),當(dāng)直接調(diào)用時(shí)居然發(fā)現(xiàn)已經(jīng)給我們封裝了,接下來我們?cè)賮硇薷闹付ǖ膶傩跃妥兂闪巳缦拢?/p>

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public IActionResult Index()
        {            var blog = new Blog() { Id = 1, Name = "EntityFramework Core 1.1" };
            _blogRepository.Update(blog, d => d.Name);
            _blogRepository.Commit();            return Ok();
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

上述只是演示,實(shí)際項(xiàng)目當(dāng)中時(shí)我們只需給出我們修改的主鍵和實(shí)體即可。如果是修改實(shí)體集合的話,再重載一個(gè)遍歷就ok。到這里你是不是發(fā)現(xiàn)已經(jīng)非常完美了,還有更完美的解決方案,請(qǐng)繼續(xù)往下看。

(3)更新方式三

其實(shí)在ASP.NET Core MVC中有比上面進(jìn)一步還爽的方式通過利用TryUpdateModelAsync方法來實(shí)現(xiàn),此方法有多個(gè)重載來實(shí)現(xiàn),完全不需要我們?nèi)シ庋b。如下:

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public async Task<IActionResult> Index()
        {            var blog = _blogRepository.GetSingle(d => d.Id == 1);
            blog.Name = "EntityFramework Core 1.1";            await TryUpdateModelAsync(blog, "", d => d.Name);
            _blogRepository.Commit();            return Ok();
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

上述三種更新方式各有其應(yīng)用場(chǎng)景,如果必須要總結(jié)的話就主要是第二種方式和第三種方式該如何取舍,第二種方式通過我們手動(dòng)封裝的方式不需要再進(jìn)行查詢,直接更改其狀態(tài)進(jìn)行提交更新即可,而第三種方式需要進(jìn)行查詢才會(huì)被追蹤最終提交更新,看個(gè)人覺得哪種方式更加合適就取哪種吧。關(guān)于EF Core 1.1中對(duì)于數(shù)據(jù)更新我們就講解完了,我們?cè)賮砜纯磩h除。

Remove/RemoveRange

對(duì)于上述和更新一樣如果該實(shí)體已經(jīng)被變更追蹤,直接調(diào)用內(nèi)置的方法Delete方法即可,大部分場(chǎng)景下是根據(jù)主鍵去刪除數(shù)據(jù)。這里有兩種方式供我們選擇,請(qǐng)往下看。

(1)刪除方式一

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public  IActionResult Index()
        {            var blog = _blogRepository.GetSingle(d => d.Id == 1);
            _blogRepository.Delete(blog);
            _blogRepository.Commit();            return Ok();
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

我們查詢出需要?jiǎng)h除的實(shí)體,然后通過調(diào)用Remove(這里我封裝了)方法將其標(biāo)識(shí)為Deleted狀態(tài)進(jìn)行刪除,當(dāng)查詢數(shù)據(jù)我們可以關(guān)閉變更追蹤,一來數(shù)據(jù)量大的話對(duì)內(nèi)存壓力不會(huì)太大,二來因?yàn)檎{(diào)用Remove方法會(huì)將其標(biāo)識(shí)為Deleted狀態(tài)也會(huì)被追蹤,不會(huì)有任何問題。

(2)刪除方式二【推薦】

為了盡量減少請(qǐng)求時(shí)間,我們能一步完成的何必要用兩步呢,我們完全可以直接實(shí)例化一個(gè)實(shí)體,將其主鍵賦值,最后修改其狀態(tài)為Deleted,最終將持久化到數(shù)據(jù)庫中刪除對(duì)應(yīng)的數(shù)據(jù)。如下:

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public  IActionResult Index()
        {            var blog = new Blog() { Id = 1 };
            _blogRepository.Delete(blog);
            _blogRepository.Commit();            return Ok();
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

Query 

最后還剩下一個(gè)查詢沒有講述,這個(gè)和添加方法一樣,比較簡(jiǎn)單我們稍微過一下即可。由于在EF Core中不再支持延遲加載,所以我們需要通過Include顯式獲取我們需要的導(dǎo)航屬性,比如如下:

 DbContext dbContext;
 dbContext.Set<Blog>().Include(d => d.Posts);

如果有多個(gè)導(dǎo)航屬性,我們接著進(jìn)行ThenInclude,如下:

            DbContext dbContext;
            dbContext.Set<Blog>().AsNoTracking().Include(d => d.Posts).ThenInclude(d => d....).

為了避免這樣多次ThenInclude,方便調(diào)用我們進(jìn)行如下封裝即可:

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

        public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
        {
           
            IQueryable<T> query = _context.Set<T>();            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }            return query.Where(predicate).FirstOrDefault();
        }

 EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

此時(shí)我們只需要進(jìn)行對(duì)應(yīng)調(diào)用即可,大概如下:

               _blogRepository.GetSingle(
               d=>d.Id == 1,
               p=>p.Posts,
               p=>....)

網(wǎng)站欄目:EntityFrameworkCore1.1Add、Attach、Update、Remove方法如何高效使用詳解
網(wǎng)站路徑:http://bm7419.com/article48/jdiihp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、定制網(wǎng)站電子商務(wù)、虛擬主機(jī)移動(dòng)網(wǎng)站建設(shè)、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營(yíng)