使用實體狀態

本主題將討論如何將實體新增並附加至內容,以及 Entity Framework 如何在 SaveChanges 期間處理這些實體。 Entity Framework 會在實體連線到內容時負責追蹤實體的狀態,但在已中斷連線或多層式案例中,您可以讓 EF 知道實體應該處於何種狀態。 本主題所示範的技巧同樣適用於使用 Code First 和 EF 設計工具所建立的模型。

實體狀態和 SaveChanges

實體可以是 EntityState 列舉所定義的五種狀態之一。 這些狀態如下:

  • 已新增:內容正在追蹤實體,但尚未存在於資料庫中
  • 未變更:實體正由內容追蹤並存在於資料庫中,而且其屬性值並未從資料庫中的值變更
  • 已修改:實體正由內容追蹤並存在於資料庫中,並已修改其部分或所有屬性值
  • 已刪除:實體正由內容追蹤並存在於資料庫中,但在下次呼叫 SaveChanges 時,已標示要從資料庫刪除
  • 中斷連結:內容未追蹤實體

SaveChanges 會針對處於不同狀態的實體執行不同動作:

  • SaveChanges 不會碰到未變更的實體。 更新不會針對處於未變更狀態的實體傳送至資料庫。
  • 新增的實體會插入資料庫中,然後在 SaveChanges 傳回時變成 [未變更]。
  • 修改過的實體會在資料庫中更新,然後在 SaveChanges 傳回時變成 [未變更]。
  • 已刪除的實體會從資料庫刪除,然後從內容中斷連結。

下列範例顯示可以變更實體或實體圖形狀態的方式。

將新的實體新增至內容

您可以在 DbSet 上呼叫 Add 方法,將新的實體新增至內容。 這會將實體放入 [已新增] 狀態,這表示會在下次呼叫 SaveChanges 時插入資料庫。 例如:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Blogs.Add(blog);
    context.SaveChanges();
}

將新實體新增至內容的另一種方式是將其狀態變更為 [已新增]。 例如:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Entry(blog).State = EntityState.Added;
    context.SaveChanges();
}

最後,您可以將新實體連結至另一個已追蹤的實體,以將新實體新增至內容。 這可能是藉由將新實體新增至另一個實體的集合導覽屬性,或藉由設定另一個實體的參考導覽屬性來指向新實體。 例如:

using (var context = new BloggingContext())
{
    // Add a new User by setting a reference from a tracked Blog
    var blog = context.Blogs.Find(1);
    blog.Owner = new User { UserName = "johndoe1987" };

    // Add a new Post by adding to the collection of a tracked Blog
    blog.Posts.Add(new Post { Name = "How to Add Entities" });

    context.SaveChanges();
}

請注意,如果新增的實體具有尚未追蹤的其他實體參考,則這些新實體也會新增至內容,並在下次呼叫 SaveChanges 時插入資料庫。

將現有的實體附加至內容

如果您有一個您知道的實體已存在於資料庫中,但目前並未由內容追蹤,則可以告訴內容使用 DbSet 上的 Attach 方法追蹤實體。 實體會處於內容中的 [未變更] 狀態。 例如:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);

    // Do some more work...  

    context.SaveChanges();
}

請注意,如果呼叫 SaveChanges 而不需要對附加實體執行任何其他操作,則不會對資料庫進行任何變更。 這是因為實體處於未變更狀態。

將現有實體附加至內容的另一種方式是將其狀態變更為 [未變更]。 例如:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

請注意,針對這兩個範例,如果附加的實體具有尚未追蹤之其他實體的參考,這些新實體也會附加至處於未變更狀態的內容。

將現有但已修改的實體附加至內容

如果您的實體已經存在於資料庫中,但可能已進行哪些變更,您可以告訴內容附加實體,並將其狀態設定為 [已修改]。 例如:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

當您將狀態變更為 [已修改] 時,實體的所有屬性都會標示為已修改,而且呼叫 SaveChanges 時,所有屬性值都會傳送至資料庫。

請注意,如果附加的實體具有尚未追蹤之其他實體的參考,這些新實體將會附加至處於未變更狀態的內容,而不會自動進行修改。 如果您有多個必須標示為 [修改] 的實體,您應該個別設定每個實體的狀態。

變更追蹤實體的狀態

您可以藉由在其專案上設定 State 屬性,來變更已追蹤的實體狀態。 例如:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

請注意,針對已追蹤的實體呼叫 Add 或 Attach 也可以用來變更實體狀態。 例如,針對目前處於 [新增] 狀態的實體呼叫 Attach,將會將其狀態變更為 [未變更]。

插入或更新模式

某些應用程式的常見模式是根據主鍵的值,將實體新增為新的實體(導致資料庫插入),或將實體附加為現有實體,並將其標示為已修改的(導致資料庫更新)。 例如,使用資料庫產生的整數主鍵時,通常會將具有零索引鍵的實體視為 new,並將非零索引鍵的實體視為現有。 您可以藉由根據主鍵值的檢查來設定實體狀態來達成此模式。 例如:

public void InsertOrUpdate(Blog blog)
{
    using (var context = new BloggingContext())
    {
        context.Entry(blog).State = blog.BlogId == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;

        context.SaveChanges();
    }
}

請注意,當您將狀態變更為 Modified 時,實體的所有屬性都會標示為已修改,而且呼叫 SaveChanges 時,所有屬性值都會傳送至資料庫。