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]; }]; }