1. 程式人生 > >Attach陷阱(Entity Framework)

Attach陷阱(Entity Framework)

AppBox 是基於 FineUI 的通用許可權管理框架,包括使用者管理、職稱管理、部門管理、角色管理、角色許可權管理等模組。

Attach方法

前面我們已經多次使用Attach方法,上一次使用Attach方法修改使用者所屬部門的程式碼如下所示:

if (String.IsNullOrEmpty(hfSelectedDept.Text))
{
    item.Dept = null;
}
else
{
    int newDeptID = Convert.ToInt32(hfSelectedDept.Text);
    if (item.Dept.DeptID != newDeptID)
    {
        Dept newDept = new Dept { DeptID = newDeptID };
        DB.Depts.Attach(newDept);
        item.Dept = newDept;
    }
}

其中 newDeptID 是通過彈出視窗或者下拉列表選擇的部門ID,也就是說這個 newDeptID 其實是存在於資料庫中的,只不過還沒有被載入到記憶體中。

這種情況非常適合使用 Attach 方法,從而避免了一次資料庫查詢來生成Dept物件:

Dept dept = DB.Depts.Where(d => d.ID == newDeptID).FirstOrDefault();

取而代之,我麼使用Attach方法,就好像這個部門已經被載入到記憶體中一樣:

Dept newDept = new Dept { DeptID = newDeptID };
DB.Depts.Attach(newDept);

Attach is used to repopulate a context with an entity that is known to already exist in the database. SaveChanges will therefore not attempt to insert an attached entity into the database because it is assumed to already be there. Entities that are already in the context in some other state will have their state set to unchanged. Attach is a no-op if the entity is already in the context in the unchanged state.

  

簡單的翻譯:Attach用來將某個已知存在於資料庫中的實體重新載入到上下文中。SaveChanges不會嘗試將Attached的實體插入到資料庫中,因為這個實體假設已經存在於資料庫中。  

Attach陷阱

為了更好的說明使用Attach過程中可能會遇到的問題,我麼從如下簡單的例子入手:

User admin = DB.Users.Where(u => u.Name == "admin").FirstOrDefault();

Dept dept = new Dept { ID = 1 };
DB.Depts.Attach(dept);

admin.Dept = dept;
DB.SaveChanges();

這個示例完成了如下操作:

1. 從資料庫中載入使用者名稱為 admin 的使用者;

2. 將ID為1的部門附加到EF上下文中;

3. 設定admin使用者所屬的部門為上述部門;

4. 儲存改變。

如果第一次執行這段程式碼,會有兩個對資料庫的操作,如下圖所示:

  

如果第二次執行這段程式碼,由於已經將使用者資料載入到EF上下文,所以對使用者部門的更新不會做任何改變,資料庫操作只有一次查詢操作:

一切看似沒有任何問題,直到我們遇到如下程式碼:

DB.Depts.Find(1);

User admin = DB.Users.Where(u => u.Name == "admin").FirstOrDefault();

Dept dept = new Dept { ID = 1 };
DB.Depts.Attach(dept);

admin.Dept = dept;
DB.SaveChanges();

在這段程式碼中,ID為1的部門已經被載入到EF上下文中,此時再次Attach同一個物件,就會出錯:

  

跳出Attach陷阱

為了方便程式碼呼叫,我們在頁面基類PageBase中增加了一個Attach方法:

// 附加實體到資料庫上下文中(首先在Local中查詢實體是否存在,不存在才Attach,否則會報錯)
protected T Attach<T>(int keyID) where T : class, IKeyID, new()
{
	T t = DB.Set<T>().Local.Where(x => x.ID == keyID).FirstOrDefault();
	if (t == null)
	{
		t = new T { ID = keyID };
		DB.Set<T>().Attach(t);
	}
	return t;
}

因此完成上述示例正確的程式碼為:

DB.Depts.Find(1);

User admin = DB.Users.Where(u => u.Name == "admin").FirstOrDefault();

Dept dept = Attach<Dept>(1);
admin.Dept = dept;
DB.SaveChanges();

你可能也注意到了,我們為實體類增加了一個 IKeyID 的介面,這是我們手工增加的:

public interface IKeyID
{
	int ID { get; set; }

}

Dept實體類實現了 IKeyID 介面,定義如下所示:

public class Dept : IKeyID
{
	[Key]
	public int ID { get; set; }

	[Required, StringLength(50)]
	public string Name { get; set; }

	[Required]
	public int SortIndex { get; set; }

	[StringLength(500)]
	public string Remark { get; set; }

	
	
	public virtual Dept Parent { get; set; }
	public virtual ICollection<Dept> Children { get; set; }


	public virtual ICollection<User> Users { get; set; }

}

深入理解Attach

1. DBContext的作用域 

大家要理解一點,之所以出現上述異常,是因為我們將 DBContext 的例項儲存在 HttpContext 中,在本系列的第一篇文章就有描述(One DbContext per Request)。

因此,我們很難在Attach時得知此物件是否已經被載入到EF的上下文中。相反,如果使用如下程式碼,則可能就不會遇到那個異常了(我們明確知道DBContext的作用域,並知道其中載入了哪些實體物件):

using(var db = new AppBoxContext())
{
	db.Depts.Find(1);
}

using(var db = new AppBoxContext())
{
	User admin = db.Users.Where(u => u.Name == "admin").FirstOrDefault();

	Dept dept = new Dept { ID = 1 };
	db.Depts.Attach(dept);
	admin.Dept = dept;
	
	db.SaveChanges();
}

2. 不要對Attach抱有過多幻想

有些時候,我們可能對Attach的期望值過高了,比如下面程式碼:

User admin = DB.Users.Where(u => u.Name == "admin").FirstOrDefault();

Dept dept = new Dept { Name = "研發部" };
DB.Depts.Attach(dept);

admin.Dept = dept;
DB.SaveChanges();

我們本來希望是將admin使用者的部門設為“研發部”,可惜這段程式碼會報錯:

看錯誤提示,我們知道是執行的SQL語句違反了外來鍵約束。

檢視執行的SQL語句,我們會發現EF試圖將ID為0的部門更新到使用者表。實際上,Depts表不存在ID為0的部門!

其實,我們建立部門的程式碼:

Dept dept = new Dept { Name = "研發部" };

建立了一個ID為0的部門。EF並不會對此有效性進行檢查,更不會查詢資料庫獲取此部門的ID。EF的會假設這個實體已經存在於資料庫中了,而我們開發人員需要保證這個假設成立!  

下載或捐贈AppBox

2. AppBox v3.0 是捐贈軟體,你可以通過捐贈作者來獲取AppBox v3.0的全部原始碼(http://fineui.com/donate/)。

相關推薦

Attach陷阱Entity Framework

AppBox 是基於 FineUI 的通用許可權管理框架,包括使用者管理、職稱管理、部門管理、角色管理、角色許可權管理等模組。 Attach方法 前面我們已經多次使用Attach方法,上一次使用Attach方法修改使用者所屬部門的程式碼如下所示: if (String.IsNullOrEmp

【轉】在使用實體框架Entity Framework的應用中加入審計信息Audit trail跟蹤數據的變動

要求 date ted hang ng- tar () eat code 在一些比較重要的業務系統中,通常會要求系統跟蹤數據記錄的變動情況。系統要記錄什麽時間,什麽人,對那些信息進行了變動。 比較簡單的實現方式是在每個表中加入兩個字段CreatedBy和CreatedA

C#:實體框架EFentity framework

本文來自:http://www.cnblogs.com/xuf22/articles/5513283.html 一、什麼是Entity Framework     微軟官方提供的ORM工具,ORM讓開發人員節省資料庫訪問的程式碼時間,將更多的時間放到業務邏輯層程式碼上。EF提供變更跟蹤

Any與All的用法Entity Framework

AppBox 是基於 FineUI 的通用許可權管理框架,包括使用者管理、職稱管理、部門管理、角色管理、角色許可權管理等模組。 屬於某個角色的使用者列表(Any的用法) 使用Subsonic,我們有兩種方法獲取屬於某個角色的使用者列表,分別是表關聯和子查詢。 Subsonic的表關聯實現:

關聯表查詢與更新Entity Framework

AppBox 是基於 FineUI 的通用許可權管理框架,包括使用者管理、職稱管理、部門管理、角色管理、角色許可權管理等模組。 關聯表的查詢操作 使用 Include 方法,我們可以在一次資料庫查詢中將關聯表的資料一併取出。 比如查詢線上使用者列表頁面,需要在前端顯示關聯的使用者資訊,如下所

如何向OrderBy傳遞字串引數Entity Framework

AppBox 是基於 FineUI 的通用許可權管理框架,包括使用者管理、職稱管理、部門管理、角色管理、角色許可權管理等模組。 Entity Framework提供的排序功能  再來回顧一下上篇文章,載入使用者列表並進行排序資料庫分頁的程式碼: var q = DB.Users.Incl

ASP.NET網站開發--實體框架EFEntity Framework

1、什麼是Entity Framework! 它就是微軟提供的ORM工具,ORM讓開發人員節省訪問資料庫的時間,將更多的時間放在業務邏輯程式碼層中,EF提供變更追蹤,唯一性約束,惰性載入,查詢事物等!開發人員使用Linq語言對資料庫操作如同操作Object一樣省事。 2、

NHibernate與EFEntity Framework的區別

概述長久以來,程式設計師和資料庫總是保持著一種微妙的關係,在商用應用程式中,資料庫一定是不可或缺的元件,這讓程式設計師一定要為了連線與訪問資料庫而去學習 SQL 指令,至少對於我而言,我覺得這是一個很不爽的事情。因此在資訊業中有很多人都在研究如何將程式設計模型和資料庫整合在一

C#綜合揭祕——利用泛型與反射更新實體ADO.NET Entity Framework

自從ADO.NET Entity Framework面世以來,受到大家的熱捧,它封裝了大量程式碼生成的工具,使用者只需要建立好實體之間的關係,系統就是會為使用者自動成功了Add、Delete、CreateObject、Attach、ToList......等等方法,這些方法

網站分頁功能的實現Entity Framework和ADO.NET兩種綜述

專案中用到了分頁,上次是用的是Entity Framework,這次用ADO.NET,都是老師講的,有必要總結一下,加深下記憶。 一、Entity Framework中完成分頁 老師就講了一種,在從資料庫倒序查詢到想要的list後,在對應的使用者控制元件的.cs檔案中用了兩

Entity Framework Core入門二、EFCore數據庫配置生成

model res ise runtime 準備 cookie bili request data 延續上一章節https://www.cnblogs.com/dzw159/p/10646368.html 我們準備將按照AspCore的依賴註入機制獲取appsetting

並行程式設計Parallel Framework

前言 並行程式設計:通過編碼方式利用多核或多處理器稱為並行程式設計,多執行緒概念的一個子集。 並行處理:把正在執行的大量的任務分割成小塊,分配給多個同時執行的執行緒。多執行緒的一種。 並行程式設計分為如下幾個結構: 1.並行的LINQ或PLINQ 2.Parallel類 3.任務並行結構 4.併

提高你的Java程式碼質量吧:避免基本型別陣列轉換列表陷阱Arrays.asList()

一、分析我們在開發的過程中經常會使用Arrays和Collections這兩個工具類在陣列和列表之間轉換。Arrays.asList()方法:輸入一個變長引數,返回一個固定長度的列表。看原始碼:publ

Hibernate中實體型別Entity Type與值型別Value Type的概念分析

Hibernate中的實體型別和值型別,大家在實際應用中都有一個大致的概念,但是如何更明確的給他們下一個合適的定義呢? 我們需要從Hibernate看待各種物件出發。 物件內有用於標識用的屬性(一般都

Entity Framework Code First Data Annotations

pos .cn ase image 希望 編程 create str length Entity Framework Code First 利用一種被稱為約定(Conventions)優於配置(Configuration)的編程模式允許你使用自己的 domain class

Entity Framework學習筆記——EF簡介一篇文章告訴你什麽是EF

比較 編程 ast 定義 .aspx b2c 文件創建 發送 ase Entity Framework是以ADO.NET為基礎,面向數據的“實體框架”。以下簡稱EF。 它利用了抽象化數據結構的方式,將每個數據庫對象都轉換成應用程序對象 (entity),

Entity Framework:使用特性數據註解創建表結構

int32 概念 ann 應該 etime max 繼承 兩個 width 一、理解Code First及其約定和配置   傳統設計應用的方式都是由下而上的,即我們習慣優先考慮數據庫,然後使用這個以數據為中心的方法在數據之上構建應用程序。這種方法非常適合於數據密集的應用或者

Entity Framework 6如何進行導航屬性的篩選context.Msg.First(t=>t.Id==1).Include(t=>t.MsgDetail),篩選MsgDetail帶條件

border .config onf cccccc 取消 ram bold src -i 問題: https://q.cnblogs.com/q/98333/ Msg表(Id,Content,IsDel)。內有 virtual ICollection<MsgDet

Entity Framework

重復 取數 \n wan 條件 數據 對象屬性 方法 升序 相關知識點復習: 1、var 類型推斷: var p=new Person(); 2、匿名類型: var a=new {Name="wang",Age=12 };

Entity Framework

關系 標註 操作數 因此 entity req tex ber 階段 1、 ORM :Object Relation Mapping ,通俗說:用操作對象的方式來操作數據庫。 2、 插入數據不再是執行Insert,而是類似於 Person p=new Person() ;