在實現基礎的三層開發的時候,你們時常會在數據層對每一個實體進行CRUD的操做,其中存在至關多的重複代碼。爲了減小重複代碼的出現,一般都會定義一個共用類,實現類似的操做,下面爲你們介紹一下Entity Framework時經常使用到的通用類。
首先在數據庫創建起幾個關聯表:Person、Company、Position,三個實體之間經過導航屬性進行相互引用。數據庫
下面爲你們分別介紹以泛型實現的 Create、Read、Update、Delete 操做:app
1. Createide
在ObjectContext類之中,早已經爲你們預約了一個Create 的操做 AddObject:測試
void ObjectContext.AddObject(entitySetName string,object entity)
void ObjectSet<T>.AddObject(T entity)ui
1 public int Add<T>(T entity) where T : EntityObject 2 { 3 int changedCount = 0; 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 context.AddObject(typeof(T).Name, entity); 9 changedCount = context.SaveChanges(); 10 if (changedCount > 0) 11 context.AcceptAllChanges(); 12 } 13 } 14 catch (Exception ex) 15 { ........ } 16 return changedCount; 17 }
從下面的測試能夠看到,ObjectContext.AddObject(entitySetName string,object entity)已至關成熟,它不但能夠加入單個實體,也可經過導航屬性,一次性加入多個關聯實體。spa
1 static void Main(string[] args) 2 { 3 BaseCommand command = new BaseCommand(); 4 //創建關聯實體 5 Company company = new Company() { CompanyName = "Sun" 6 ,Address="Beijing",Telephone="010-87654321"}; 7 Position position = new Position() { PositionName = "Project Manager" 8 , Salary = 15000.00, Company = company }; 9 //經過Add<T>同時加入實體對象company與position 10 int n=command.Add<Position>(position); 11 12 Console.ReadKey(); 13 }
若要使用批量插入,只要在AddObject方法前多加一個重複語言便可,在此就再也不多做解釋了。scala
1 public int AddList<T>(List<T> entityList) where T : EntityObject 2 { 3 int changedCount = 0; 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 foreach (T entity in entityList) 9 context.AddObject(typeof(T).Name, entity); 10 changedCount = context.SaveChanges(); 11 if (changedCount > 0) 12 context.AcceptAllChanges(); 13 } 14 } 15 catch (Exception ex) 16 { ....... } 17 return changedCount; 18 }
2. Deletecode
一樣地,ObjectContext 類當中也存在方法 ObjectContext.DeleteObject(object entity)用於刪除實體。
首先經過輸入的參數 id 創建起EntityKey對象,而後在ObjectContext查找此實體,若實體存在則使用ObjectContext.DeleteObject(object entity)方法把此實體刪除 。xml
1 public int Delete<T>(int id) where T : EntityObject 2 { 3 int changedCount = 0; 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 //創建EntityKey對象 9 EntityKey entityKey = new EntityKey( 10 "BasicArchitectureEntities." + typeof(T).Name, "Id", id); 11 //經過EntityKey找到實體 12 var objResult = context.GetObjectByKey(entityKey); 13 //若實體存在則刪除實體 14 if (objResult != null) 15 context.DeleteObject(objResult); 16 changedCount = context.SaveChanges(); 17 if (changedCount > 0) 18 context.AcceptAllChanges(); 19 } 20 } 21 catch (Exception ex) 22 { ...... } 23 return changedCount; 24 }
ObjectContext.DeleteObject(object entity)與ObjectContext.AddObject(entitySetName string,object entity)相同,能夠經過導航屬性,一次性刪除多個關聯實體。但若是數據庫中存在下面的數據對象
Company表:
Position表:
此時使用此 int Delete<Company>(2) 方法刪除Company對象,系統將會報錯。這是因爲導航屬性在默認狀況下具備延時加載的特性,在系統使用ObjectContext.GetObjectByKey(entityKey)方法加載實體時,它的導航屬性不會立刻加載到上下文當中。而是在調用該導航屬性時,對象纔會被加載。
於是系統經過ObjectContext.GetObjectByKey(2)獲取Company對象時,對應的Position對象並未被加載到上下文當中,因此當刪除Company對象時,Position對象不能被同步刪除,於是形成邏輯上的錯誤。爲解決這一問題,能夠利用RelatedEnd.Load()方法提早加載導航屬性。
RelatedEnd是EntityCollection<TEntity> 、EntityReference的父類,它們是特定實體類型的對象集合,該實體類型表示一對多、多對1、多對多的關係。而RelatedEnd.Load()方法,能夠將一個或多個相關對象提早加載到相關實體當中。
首先經過ObjectContext.GetObjectByKey(entityKey)方法找到Company對象,而後利用反射屬性PropertyInfo類獲取導航屬性Position,最後使用RelatedEnd.Load()方法,把導航屬性加載到當前上下文中。此時使用Delete<Company,Position>(2)方法刪除Company對象時,系統將能正常運行,並把對應的Position對象一併刪除。
1 public int Delete<PKEntity, FKEntity>(int id) 2 where PKEntity : EntityObject 3 where FKEntity : EntityObject 4 { 5 int changedCount = 0; 6 try 7 { 8 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 9 { 10 //根據軟件Id創建EntityKey對象 11 EntityKey entityKey = new EntityKey( 12 "BasicArchitectureEntities." + typeof(PKEntity).Name, "Id", id); 13 //根據EntityKey查找對應對象 14 PKEntity objResult = context.GetObjectByKey(entityKey) as PKEntity; 15 //根據FKEntity加載導航屬性 16 PropertyInfo propertyInfo = typeof(PKEntity).GetProperty( 17 typeof(FKEntity).Name); 18 EntityCollection<FKEntity> FKEntityList = propertyInfo.GetValue( 19 objResult, null) as EntityCollection<FKEntity>; 20 21 if (FKEntityList != null) 22 FKEntityList.Load(); 23 24 if (objResult != null) 25 context.DeleteObject(objResult); 26 changedCount = context.SaveChanges(); 27 28 if (changedCount > 0) 29 context.AcceptAllChanges(); 30 } 31 } 32 catch (Exception ex) 33 { ........ } 34 return changedCount; 35 }
經過下面的方法也可根據輸入的委託predicate,批量刪除有關的數據。
1 public int Delete<T>(Func<T,bool> predicate) where T: EntityObject 2 { 3 int changedCount = 0; 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 //根據輸入的委託查找數據 9 var list = context.CreateObjectSet<T>().Where(predicate); 10 //若存在數據,刪除有關數據 11 if (list.Count() > 0) 12 foreach (var obj in list) 13 context.DeleteObject(obj); 14 15 changedCount = context.SaveChanges(); 16 if (changedCount > 0) 17 context.AcceptAllChanges(); 18 } 19 } 20 catch (Exception ex) 21 { ...... } 22 return changedCount; 23 }
與前面的例子相同,當使用 Delete<Company>(x=>x.Id==2) 方法刪除 Company 對象時,因爲導航屬性 Position 處於延遲加載的狀態,以至系統沒法實現同步刪除,從而令數據出現邏輯性的錯誤。
此時使用相似的方法,利用 RelatedEnd.Load() 把導航屬性提早加入到上下文中,再刪除Company對象時,系統就能夠把對應 Position 對象一併刪除。
1 public int Delete<PKEntity, FKEntity>(Func<PKEntity,bool> predicate) 2 where PKEntity : EntityObject 3 where FKEntity : EntityObject 4 { 5 int changedCount = 0; 6 try 7 { 8 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 9 { 10 //根據輸入的委託查找數據 11 var list = context.CreateObjectSet<PKEntity>().Where(predicate); 12 //若數目大於0,刪除有關數據 13 if (list.Count() > 0) 14 { 15 foreach (var obj in list) 16 { 17 //在刪除前加載其導航屬性 18 PropertyInfo propertyInfo = typeof(PKEntity) 19 .GetProperty(typeof(FKEntity).Name); 20 EntityCollection<FKEntity> FKEntityList = propertyInfo 21 .GetValue(obj, null) as EntityCollection<FKEntity>; 22 if (FKEntityList.Count > 0) 23 FKEntityList.Load(); 24 25 context.DeleteObject(obj); 26 } 27 } 28 changedCount = context.SaveChanges(); 29 30 if (changedCount > 0) 31 context.AcceptAllChanges(); 32 } 33 } 34 catch (Exception ex) 35 { ....... } 36 return changedCount; 37 }
此時使用Delete<Company,Position>(x=>x.Id==2),這樣就能夠把Company對象和相關的Position對象同時刪除。
3. Update
ObjectContext 中存在方法 ObjectContext.ApplyCurrentValues<TEntity> 和 ObjectContext.ApplyOriginalValues<TEntity>,用於把將標量值從實體複製到 ObjectContext 中具備相同主鍵的對象集中。
注意:在調用此方法前必須把實體預先加載到當前上下文當中,要否則系統將會顯示 「objectstatemanager 沒法跟蹤具備相同鍵的多個對象」 的錯誤。
因爲DAL層的對象大部分使用單體模式進行開發,而BaseCommand是一個共用對象,在共同操做時,Create、Delete、Read 等操做通常不會對實體形成邏輯性的影響。但若是有多個實體同時調用 Update 操做,就有可能對實體形成邏輯性影響。爲了不這一事件的發生,此處使用方法鎖定的模式,以 lock(object) 鎖定某一對象,以確保在同一時間內只會對一個實體進行更新。
首先經過反射方式獲取對象的Id,而後經過 ObjectContext.GetObjectByKey(entityKey) 方法把實體加載到當前上下文當中,最後利用 ObjectContext.ApplyCurrentValues<TEntity> 方法,把新加入的實體的屬性複製當前上下文。
1 public class BaseCommand 2 { 3 private object o = new object(); 4 5 public int Update<T>(T entity) where T : EntityObject 6 { 7 lock (o) 8 { 9 int changedCount = 0; 10 Type type = typeof(T); 11 12 try 13 { 14 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 15 { 16 //獲取實體的Id屬性 17 PropertyInfo property = type.GetProperty("Id"); 18 object id = property.GetValue(entity, null); 19 //根據Id獲取上下文中的對應實體 20 EntityKey entityKey = new EntityKey("BasicArchitectureEntities." 21 + type.Name, "Id", id); 22 var objResult = context.GetObjectByKey(entityKey); 23 //更新實體屬性 24 if (objResult != null) 25 context.ApplyCurrentValues<T>(type.Name, entity); 26 27 changedCount = context.SaveChanges(); 28 if (changedCount > 0) 29 context.AcceptAllChanges(); 30 } 31 } 32 catch (Exception ex) 33 { ... } 34 return changedCount; 35 } 36 } 37 }
在一對多,多對一關係時,也可使用如下方法進行導航屬性的同步更新。首先經過反射獲取主實體的主鍵Id,而後創建EntityKey對象,再經過ObjectContext.GetObjectByKey(entityKey)方法在當前上下文當中獲取此實體,最後經過 ObjectContext.ApplyCurrentValues<TEntity> 方法,把新加入的實體的屬性複製當前上下文。
下一步就是對導航屬性進行更新,首先經過反射獲取外鍵屬性,而後對一對多,多對一的關係進行分別處理。在一對多關係時,把導航屬性轉換成EntityCollection<T2>對象集合,而後經過 ObjectContext.ApplyCurrentValues<TEntity> 方法對集合中的每一個對象進行逐個更新。
在多對一關係時,直接把導航屬性轉換成T2類型的對象進行更新。
1 public int Update<T1, T2>(T1 entity) 2 where T1 : EntityObject 3 where T2 : EntityObject 4 { 5 lock (o) 6 { 7 int changedCount = 0; 8 Type typeT1 = typeof(T1); 9 Type typeT2 = typeof(T2); 10 try 11 { 12 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 13 { 14 PropertyInfo property = typeT1.GetProperty("Id"); 15 object id = property.GetValue(entity, null); 16 17 //根據軟件Id創建EntityKey對象 18 EntityKey entityKey = new EntityKey("BasicArchitectureEntities." 19 + typeT1.Name, "Id", id); 20 //根據EntityKey查找對應對象 21 T1 objT1 = context.GetObjectByKey(entityKey) as T1; 22 //在上下文中更新當前對象 23 if (objT1 != null) 24 context.ApplyCurrentValues<T1>(typeT1.Name, entity); 25 26 //獲取外鍵屬性 27 PropertyInfo propertyInfo = typeT1.GetProperty(typeT2.Name); 28 29 //在一對多關鍵時更新導航屬性 30 var T2List = propertyInfo.GetValue(entity, null) 31 as EntityCollection<T2>; 32 if (T2List != null) 33 { 34 foreach (var obj in T2List.ToList()) 35 { 36 var oldEntity = context.GetObjectByKey(obj.EntityKey); 37 if (oldEntity != null) 38 context.ApplyCurrentValues<T2>(typeT2.Name, obj); 39 } 40 } 41 42 //在多對一,一對一關係時更新導航屬性 43 var objT2 = propertyInfo.GetValue(entity, null) as T2; 44 if (objT2!= null) 45 { 46 var oldEntity = context.GetObjectByKey(objT2.EntityKey); 47 if (oldEntity != null) 48 context.ApplyCurrentValues<T2>(typeT2.Name, objT2); 49 } 50 51 changedCount = context.SaveChanges(); 52 if (changedCount > 0) 53 context.AcceptAllChanges(); 54 } 55 catch (Exception ex) 56 { ...... } 57 return changedCount; 58 } 59 }
經過此方法,不管你要經過Company同步更新Position,仍是反過來經過Position同步更新Company,系統也能正常運行。
4. Read
Read 是CRUD中最多見的,下面就爲你們介紹最通用的幾種方法
4.1 經過Id獲取單個實體
1 public T GetObject<T>(int id) where T : EntityObject 2 { 3 try 4 { 5 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6 { 7 EntityKey entityKey = new EntityKey("BasicArchitectureEntities." 8 + typeof(T).Name, "Id", id); 9 var objResult = context.GetObjectByKey(entityKey); 10 return objResult as T; 11 } 12 } 13 catch (Exception ex) 14 { 15 return null; 16 } 17 }
4.2 經過輸入的Func<T,bool>委託獲取對象
1 public T GetObject<T>(Func<T,bool> predicate) where T : EntityObject 2 { 3 try 4 { 5 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6 { 7 var objectSet = context.CreateObjectSet<T>().Where(predicate); 8 if (objectSet.Count() > 0) 9 return objectSet.First(); 10 else 11 return null; 12 } 13 } 14 catch (Exception ex) 15 { 16 return null; 17 } 18 }
4.3經過輸入的Func<T,bool>委託獲取對象,並同時加載單個導航屬性
1 public T GetObject<T>(Func<T, bool> predicate,string includePath) 2 where T : EntityObject 3 { 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 var objectQuery = context.CreateObjectSet<T>() 9 .Include(includePath) 10 .Where(predicate); 11 12 if (objectQuery.Count() > 0) 13 return objectQuery.First(); 14 else 15 return null; 16 } 17 } 18 catch (Exception ex) 19 { 20 return null; 21 } 22 }
4.4經過輸入的Func<T,bool>委託獲取對象,並同時加載多個導航屬性
1 public T GetObject<T>(Func<T, bool> predicate, string[] includePath) 2 where T : EntityObject 3 { 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 var list = context.CreateObjectSet<T>().Where("1==1"); 9 10 foreach (var path in includePath) 11 list=list.Include(path); 12 13 var returnValue = list.Where(predicate).ToList(); 14 15 if (returnValue.Count() > 0) 16 return returnValue.First(); 17 else 18 return null; 19 } 20 } 21 catch (Exception ex) 22 { 23 return null; 24 } 25 }
4.5 經過輸入的Func<T,bool>委託獲取對象集合
1 public IList<T> GetList<T>(Func<T,bool> func) where T:EntityObject 2 { 3 try 4 { 5 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6 { 7 ObjectSet<T> objectSet = context.CreateObjectSet<T>(); 8 IList<T> list = objectSet.Where(func).ToList(); 9 return list; 10 } 11 } 12 catch (Exception ex) 13 { 14 return null; 15 } 16 }
4.6經過輸入的Func<T,bool>委託獲取對象集合,並同時加入單個導航屬性
1 public IList<T> GetList<T>(Func<T, bool> func,string includePath) 2 where T : EntityObject 3 { 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 ObjectSet<T> objectSet = context.CreateObjectSet<T>(); 9 IList<T> list = objectSet.Include(includePath).Where(func).ToList(); 10 return list; 11 } 12 } 13 catch (Exception ex) 14 { 15 return null; 16 } 17 }
4.7經過輸入的Func<T,bool>委託獲取對象集合,並同時加入多個導航屬性
1 public IList<T> GetList<T>(Func<T, bool> func, string[] includePath) 2 where T : EntityObject 3 { 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 var list = context.CreateObjectSet<T>().Where("1==1"); 9 foreach (var path in includePath) 10 list = list.Include(path); 11 return list.Where(func).ToList(); 12 } 13 } 14 catch (Exception ex) 15 { 16 return null; 17 } 18 }
4.8 經過原始的SqlCommandText獲取對象集
1 public IList<T> GetList<T>(string commandText) 2 { 3 try 4 { 5 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6 { 7 IList<T> list = context.ExecuteStoreQuery<T>(commandText).ToList(); 8 return list; 9 } 10 } 11 catch (Exception ex) 12 { 13 return null; 14 } 15 }
只能完成這一個DAL層的通用類之後,您就可在CompanyDAL、PersonDAL、PositionDAL ...... 等多個類中調用這個通用類,輕鬆地完成各項CRUD的操做。
1 public class CompanyDAL:ICompanyDAL 2 { 3 private BaseCommand command = new BaseCommand(); 4 5 public int AddCompany(Company company) 6 { 7 return command.Add<Company>(company); 8 } 9 10 public int DeleteCompany(int id) 11 { 12 return command.Delete<Company>(id); 13 } 14 15 public int UpdateComapny(Company company) 16 { 17 return command.Update<Company>(company); 18 } 19 ............. 20 }
相比起以往的SqlCommand操做,Entity Framework更體現出映射的靈活性。以往的操做中,即便開發出一個通用類,CommandText 一般都須要使用手工輸入,特別是重複的Update命令操做中,每每使人不厭其煩。經過Entity Framework能夠把CRUD更高度地集中在一個通用類,令開發變得更加簡單。