NET基礎篇——Entity Framework 數據轉換層通用類

 

在實現基礎的三層開發的時候,你們時常會在數據層對每一個實體進行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更高度地集中在一個通用類,令開發變得更加簡單。

相關文章
相關標籤/搜索