一 先發問。
問題:在使用EF過程中,能否有一個方法可以直接執行傳入的SQL語句。糾結的只找到了調用存儲過程的方法,難道要SqlHelper.cs?
二 友情提示
本文內容參考自MSDN。
三 言歸正傳
平時使用MVC 開發時,在CRUD相關的Action當中,都會有在最后調用一句代碼:
db.SaveChanges();// Entities db=new Entities()
這個方法會根據當前欲操作的實體(Entity)所處的狀態(State)與數據庫交互。單單從名字上可以看出這個方法是為了“保存改變”,在現實中,一位你好久沒見的朋友有可能會這樣說:“小王啊,好久沒見,你變胖了,是不是賺大錢了,生活安逸嘍?”姑且不論你是不是張的長胖了,還是賺大錢了,這里面“胖”就是用來表征你當前所處的狀況的一個描述。在命名空間System.Data當中,也有一個描述實體所處狀態的枚舉類型EntityState:

// 摘要: // 實體對象的狀態。 [Flags] public enum EntityState { // 摘要: // 對象存在,但未由對象服務跟蹤。在創建實體之后、但將其添加到對象上下文之前,該實體處於此狀態。通過調用 System.Data.Objects.ObjectContext.Detach(System.Object) // 方法從上下文中移除實體后,或者使用 System.Data.Objects.MergeOption.NoTrackingSystem.Data.Objects.MergeOption // 加載實體后,該實體也會處於此狀態。 Detached = 1, // // 摘要: // 自對象加載到上下文中后,或自上次調用 System.Data.Objects.ObjectContext.SaveChanges() 方法后,此對象尚未經過修改。 Unchanged = 2, // // 摘要: // 對象已添加到對象上下文,但尚未調用 System.Data.Objects.ObjectContext.SaveChanges() 方法。對象是通過調用 // System.Data.Objects.ObjectContext.AddObject(System.String,System.Object) // 方法添加到對象上下文中的。 Added = 4, // // 摘要: // 使用 System.Data.Objects.ObjectContext.DeleteObject(System.Object) 方法從對象上下文中刪除了對象。 Deleted = 8, // // 摘要: // 對象已更改,但尚未調用 System.Data.Objects.ObjectContext.SaveChanges() 方法。 Modified = 16, }
這五種狀態分別是:Detached-游離;UnChanged-沒有變化;Added-添加;Deleted-刪除;Modified-編輯。Detached狀態下的Entity不會被上下文(context)所捕獲(track),比如說下面這種情況下:
public JsonResult Edit(UserModel source) { if (this.ModelState.IsValid) { User destination = new User(); UserToEntity(source, destination); destination.ID = source.ID; db.Entry(destination).State = System.Data.EntityState.Modified; db.SaveChanges(); return Json(new { isok = true, tip = "修改成功" }); } else { return Json(new { isok = false, tip = "添加用戶失敗" }); } }
在db.Entry(destination)……這句代碼執行之前,destination的State狀態就是游離。
除了游離狀態,剩下的四種狀態均會被上下文所捕獲,具體意思也很好理解。
當SavaChanged()方法執行期間,他會查看當前Entity的EntityState的值,決定是去新增(Added)、修改(Modified)、刪除(Deleted)、什么也不做(UnChanged)。
回到正題,DbContext類的Add()方法的作用就是將一個Entity的State修改為Added,這樣在SavaChanged()方法就會將實體新增到數據庫當中。而Attach在 微軟的中文翻譯中是附加,不同於Add方法的添加,她是將一個處於Detached的Entity附加到上下文,而附加到上下文后的這一Entity的State為UnChanged,所以在下面的代碼中,需要將obj的State修改為Modified:
/// <summary> /// 更新Entity(注意這里使用的傻瓜式更新,可能性能略低) /// </summary> /// <param name="model"></param> /// <returns></returns> public virtual bool Update(T entity) { var obj = db.Set<T>();//新建一個泛型DbSet obj.Attach(entity);//附加到上下文 db.Entry(entity).State = System.Data.EntityState.Modified;//修改State return db.SaveChanges() > 0; }
新建的obj Entity因為出於Detached,所以我們需要先附加后修改其EntityState;
搞清楚這點,完成添加功能的Action 是不是可以這樣寫呢:
public virtual bool Add(T entity) { var obj = db.Set<T>(); obj.Attach(entity); db.Entry(entity).State = System.Data.EntityState.Added; return db.SaveChanges() > 0; }
同樣的,刪除的Remove方法也可以不用,轉而修改其State為Deleted。
最后,那就可以用Action完成Add或Update的功能:
public void InsertOrUpdate(Blog blog) { using (var context = new BloggingContext()) { context.Entry(blog).State = blog.BlogId == 0 ? EntityState.Added : EntityState.Modified; context.SaveChanges(); } }
以往增加刪除都是用一個相同的部分視圖加上不同的控制器實現,現在只需要在一個控制器就能實現增加和修改了。
四 Context
上下文是一個不太好描述清楚的東東,我是這樣簡單理解的:有一些方法,在參數中不方便,也不可能獲取到的東東(請原諒我這樣形容),就可以將其存儲在上下文中。比如我這個方法需要知道當前用戶是誰,需要知道這次請求來自於哪一個Controller,就可以調用不同的Context獲得。在MVC中有很多上下文類,列如這篇博文總結的MVC上下文;