Entity Framework 5.0系列之資料操作

KenshinCui發表於2013-09-29

Entity Framework將概念模型中定義的實體和關係對映到資料來源,利用實體框架可以將資料來源返回的資料具體化為物件;跟蹤物件所做的更改;併發處理;將物件更改傳播到資料來源等。今天我們就一起討論如何利用Entity Framework進行查詢、插入、更新和刪除資料。

查詢

我們將使用AdventureWorks資料庫來進行今天的所有演示,因此開始之前請準備好相應的資料庫。在EF中進行查詢應該說是相當簡單,只需要定義一個類繼承於“DbContext”,然後定義對應的“DbSet”集合屬性即可。例如下面的“AdventureWorksContext”類:

using System.Data.Entity; using System.Data.Entity.Infrastructure; using EFPowerTools.Models.Mapping; namespace EFPowerTools.Models { public partial class AdventureWorksContext : DbContext { static AdventureWorksContext() { Database.SetInitializer<AdventureWorksContext>(null); } public AdventureWorksContext() : base("Name=AdventureWorksContext") { } public DbSet<Employee> Employees { get; set; } public DbSet<Person> People { get; set; } } }

一個資料庫上下文的生命週期隨著該物件的建立而開始,隨著物件的釋放(或GC回收)而結束,因此建議在開發過程中使用“Using”編碼方式,這樣就可以免去手動釋放物件的操作。如下面的程式碼:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var persons = db.People.Where(p => p.LastName == "Stevens").OrderBy(p=>p.FirstName); foreach(var p in persons) { Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName); } } } } } 

 

另外對於資料庫連線的管理在EF中是透明的,我們一般不需要手動進行處理,當查詢一個物件時開啟連線當處理完查詢的結果集之後會自動關閉連線。

在EF的Code First模式中有三種常用的資料查詢方式(Model First和Database First中還有其他方式如:使用Entity SQL,但這不是我們今天的重點):Linq To Entity表示式查詢、基於方法的查詢、原生SQL查詢。

Linq To Entity表示式查詢

查詢表示式是C#3.0新增的功能,它是由一組類似於T-SQL或XQuery宣告性語句組成,CLR並不能直接讀取這種查詢表示式而是在編譯時轉換為對應的方法呼叫。如下面的例子:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var persons = from p in db.People where p.LastName == "Stevens" orderby p.FirstName select p; foreach (var p in persons) { Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName); } } } } } 

基於方法的查詢

基於方法的查詢事實上是一組物件的擴充套件方法,同Linq查詢不同的是這些方法可以直接被CLR識別並執行。

例如上面的方法我們可以轉換為如下程式碼,他們的效果是一樣的,返回的都是“IQueryable”物件:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var persons = db.People.Where(p => p.LastName == "Stevens").OrderBy(p => p.FirstName); foreach (var p in persons) { Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName); } } } } } 

原生SQL查詢

EF還支援原生SQL查詢(注意與Entity SQL區別),例如:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; using System.Data.Objects; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var persons = db.People.SqlQuery("SELECT * FROM Person.Person WHERE LastName='Stevens'"); foreach (var p in persons) { Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName); } } } } } 

不僅如此,EF還支援非實體型別的查詢:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; using System.Data.Objects; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var persons = db.Database.SqlQuery<string>("SELECT FirstName FROM Person.Person WHERE LastName='Stevens'").ToList(); foreach (var p in persons) { Console.WriteLine("FirstName:{0}", p); } } } } } 

當然也支援無返回值的SQL命令:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; using System.Data.Objects; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { db.Database.ExecuteSqlCommand("UPDATE Person.Person SET NameStyle=1 WHERE BusinessEntityID='1813'"); } } } } 

增加

在EF中新增操作一般有兩種方式:一是直接建立物件,然後呼叫“DbSet”的”Add()”方法進行新增;二是呼叫資料庫上下文的”Entry()”方法並設定對應的狀態。無論使用哪種方式最終一定要呼叫“SaveChange()”進行提交。如:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var stephen = new Person { BusinessEntityID=20778, PersonType="EM", NameStyle=false, Title="Architec", FirstName="Stephen", LastName="Chow", EmailPromotion=1, rowguid = Guid.NewGuid(), ModifiedDate = DateTime.Now }; db.People.Add(stephen); var jeffrey = new Person { BusinessEntityID = 20779, PersonType = "EM", NameStyle = false, Title = "Engineer", FirstName = "Jeffrey", LastName = "Lee", EmailPromotion = 0, rowguid=Guid.NewGuid(), ModifiedDate=DateTime.Now }; db.Entry(jeffrey).State = EntityState.Added; db.SaveChanges(); } } } } 

效果如圖:

此外,在含有導航屬性時,將一個物件賦值給另一個物件的導航屬性也能達到新增的效果(當導航屬性為”DbSet“集合時通過呼叫導航屬性的“Add()“方法也同樣可以達到新增效果),例如在”Person.Person”中我們上面新增了兩條記錄,但對於“Person”類的導航屬性“EmailAddress”和“Password”在對應的“EmailAddress”表和“Password”表中並沒有新增對應的記錄,此時我們就可以通過下面的方式來增加:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var password = new Password { BusinessEntityID=20778, PasswordHash = "ZEgQH9qZIPiLgyBHYw/dD1FJQNpdQyIAa+BFfKX5/jg=", PasswordSalt = "7iy/umc=", rowguid=Guid.NewGuid(), ModifiedDate=DateTime.Now }; var email = new EmailAddress { BusinessEntityID = 20778, EmailAddress1 = "StephenChow@outlook.com", rowguid = Guid.NewGuid(), ModifiedDate = DateTime.Now }; var person = db.People.Find(20778); person.Password = password; person.EmailAddresses.Add(email); db.SaveChanges(); } } } } 

此時檢視將可以看到“EmailAddress”表中確實增加了一條記錄(“Password”表同樣也是如此):

狀態跟蹤

在這裡我們需要強調一點那就是狀態跟蹤,對於上面的操作如果我們呼叫“Attach()”方法對實體進行跟蹤或者設定實體的狀態那麼資料將不會儲存到資料庫:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var stephen = new Person { BusinessEntityID = 20778, PersonType = "EM", NameStyle = false, Title = "Architec", FirstName = "Stephen", LastName = "Chow", EmailPromotion = 1, rowguid = Guid.NewGuid(), ModifiedDate = DateTime.Now }; db.People.Add(stephen); db.People.Attach(stephen); //db.Entry(stephen).State = EntityState.Unchanged;//同上面db.People.Attach(stephen);作用一樣 db.SaveChanges(); } } } } 

 

使用”Attach()”方法進行實體跟蹤時會設定實體的狀態為“Unchanged”此時實體處於未修改狀態,當執行“SaveChange()”方法時EF不會執行修改操作。相反如果此時設定實體狀態為“Modified”則EF執行更新操作。那麼既然EF的資料修改操作(增加、更新、刪除)是根據實體狀態而進行的,那麼為什麼之前我們的增加操作能正常進行而不用手動修改其狀態呢?原因是EF會自動發現狀態改變,在呼叫下面的方法時狀態發現是自動的:

· DbSet.Find

· DbSet.Local

· DbSet.Remove

· DbSet.Add

· DbSet.Attach

· DbContext.SaveChanges

· DbContext.GetValidationErrors

· DbContext.Entry

· DbChangeTracker.Entries

當然,並不是所有的時候我們都需要EF自動發現狀態改變,設定 “DbContext.Configuration.AutoDetectChangesEnabled”屬性為“false”可以禁用自動發現功能。

注意:在EF對資料操作時有時會丟擲:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
此時可以使用try{} catch(DbEntityValidationException ex){} 對異常進行捕獲,將滑鼠放到ex上並逐級檢視ex的資訊進行解決。

刪除

下面看一下EF的刪除操作:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; using System.Data.Objects; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var mail = db.EmailAddresses.Where(m => m.EmailAddressID == 19977).FirstOrDefault(); db.EmailAddresses.Remove(mail); db.SaveChanges(); } } } } 

當然有了上面狀態跟蹤的討論相信大家也可以想到如下刪除方法:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; using System.Data.Objects; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var mail = db.EmailAddresses.Where(m => m.EmailAddressID == 19976).FirstOrDefault(); db.Entry(mail).State = EntityState.Deleted; db.SaveChanges(); } } } } 

修改

修改資料很簡單,直接修改對應的屬性即可:

using System; using System.Collections.Generic; using System.Data; using System.Data.Entity.Validation; using System.Linq; using System.Text; using System.Threading.Tasks; using EFPowerTools.Models; using System.Data.Objects; namespace EFPowerTools { class Program { static void Main(string[] args) { using (var db = new AdventureWorksContext()) { var person = db.People.Where(p => p.LastName == "Stevens").OrderBy(p=>p.BusinessEntityID).FirstOrDefault(); person.NameStyle = false; person.EmailAddresses.First().EmailAddress1 = "KenshinCui@outlook.com"; db.SaveChanges(); } } } } 

需要說明的是,EF在執行修改操作前會檢查哪些屬性發生了變化,並且只會修改發生變化的欄位。

今天的內容就先到這裡了,從前面的EF5.0概覽到現在的資料操作,關於EF5.0基礎的入門內容已經說完了,更多內容敬請關注後面的文章。

- (void)configDelegate {
	__weak typeof(self) weakSelf = self;
	[self.delegate setSelectRowAtIndexPathBlock:^(id cell, NSIndexPath *indexPath) {
	  TMPremiereBrand *premiereBrand = (TMPremiereBrand *)weakSelf.data[indexPath.row];
	  TMSpecialTopicController *specialTopicController = [[TMSpecialTopicController alloc] init];
	  specialTopicController.premiereBrandID = premiereBrand.ID;
	  [weakSelf.navigationController pushViewController:specialTopicController animated:YES];
	}];
}

 

 

相關文章