熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

Entity Framework數據轉換層通用類

2022-06-13   來源: .NET編程 

  在實現基礎的三層開發的時候大家時常會在數據層對每個實體進行CRUD的操作其中存在相當多的重復代碼為了減少重復代碼的出現通常都會定義一個共用類實現相似的操作下面為大家介紹一下Entity Framework時常用到的通用類
   
    首先在數據庫建立起幾個關聯表PersonCompanyPosition三個實體之間通過導航屬性進行相互引用 

  


    下面為大家分別介紹以泛型實現的 CreateReadUpdateDelete 操作
    Create
    在ObjectContext類之中早已經為大家預定了一個Create 的操作 AddObject:
    void ObjectContextAddObject(entitySetName stringobject entity)
    void ObjectSet<T>AddObject(T entity)
             public int Add<T>(T entity) where T : EntityObject
             {
                 int changedCount = ;
                 using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                 {
                     try
                     {
                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                         {
                             contextAddObject(typeof(T)Name entity)
                             changedCount = contextSaveChanges()
                             if (changedCount >
                                 contextAcceptAllChanges()
                             scopeComplete()
                         }
                     }
                     catch (Exception ex)
                     { …… }
                 }
                 return changedCount;
             }
    從下面的測試可以看到ObjectContextAddObject(entitySetName stringobject entity)已相當成熟它不但可以加入單個實體也可通過導航屬性一次性加入多個關聯實體
             static void Main(string[] args)
             {
                 BaseCommand command = new BaseCommand()
                 //建立關聯實體
                 Company company = new Company() { CompanyName = Sun Address=BeijingTelephone=};
                 Position position = new Position() { PositionName = Project Manager Salary = Company = company };
                 //通過Add<T>同時加入實體對象company與position
                 int n=commandAdd<Position>(position)
   
                 ConsoleReadKey()
             }


    若要使用批量插入只要在AddObject方法前多加一個重復語言即可在此就不再多作解釋了
             public int AddList<T>(List<T> entityList) where T : EntityObject
             {
                 int changedCount = ;
                 using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                 {
                     try
                     {
                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                         {
                             foreach (T entity in entityList)
                                 contextAddObject(typeof(T)Name entity)
                             changedCount = contextSaveChanges()
                             if (changedCount >
                                 contextAcceptAllChanges()
                             scopeComplete()
                         }
                     }
                     catch (Exception ex)
                     { …… }
                 }
                 return changedCount;
             }
    Delete
    同樣地ObjectContext 類當中也存在方法 ObjectContextDeleteObject(object entity)用於刪除實體
    首先通過輸入的參數 id 建立起EntityKey對象然後在ObjectContext查找此實體若實體存在則使用ObjectContextDeleteObject(object entity)方法把此實體刪除
             public int Delete<T>(int id) where T : EntityObject
             {
                 int changedCount = ;
                 using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                 {
                     try
                     {
                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                         {
                             //建立EntityKey對象
                             EntityKey entityKey = new EntityKey(BasicArchitectureEntities + typeof(T)Name Id id)
                             //通過EntityKey找到實體
                             var objResult = contextGetObjectByKey(entityKey)
                             //若實體存在則刪除實體
                             if (objResult != null)
                                 contextDeleteObject(objResult)
                             changedCount = contextSaveChanges()
                             if (changedCount >
                                 contextAcceptAllChanges()
   
                             scopeComplete()
                         }
                     }
                     catch (Exception ex)
                     { …… }
                 }
                 return changedCount;
             }
    ObjectContextDeleteObject(object entity)與ObjectContextAddObject(entitySetName stringobject entity)相同可以通過導航屬性一次性刪除多個關聯實體但如果數據庫中存在下面的數據
    Company表
    Position表
    此時使用此 int Delete<Company>() 方法刪除Company對象系統將會報錯這是由於導航屬性在默認情況下具有延時加載的特性在系統使用ObjectContextGetObjectByKey(entityKey)方法加載實體時它的導航屬性不會馬上加載到上下文當中而是在調用該導航屬性時對象才會被加載
    因而系統通過ObjectContextGetObjectByKey()獲取Company對象時對應的Position對象並未被加載到上下文當中所以當刪除Company對象時Position對象不能被同步刪除因而造成邏輯上的錯誤為解決這一問題可以利用RelatedEndLoad()方法提前加載導航屬性
    RelatedEnd是EntityCollection<TEntity> EntityReference的父類它們是特定實體類型的對象集合該實體類型表示一對多多對一多對多的關系而RelatedEndLoad()方法可以將一個或多個相關對象提前加載到相關實體當中
    首先通過ObjectContextGetObjectByKey(entityKey)方法找到Company對象然後利用反射屬性PropertyInfo類獲取導航屬性Position最後使用RelatedEndLoad()方法把導航屬性加載到當前上下文中此時使用Delete<CompanyPosition>()方法刪除Company對象時系統將能正常運行並把對應的Position對象一並刪除
             public int Delete<PKEntity FKEntity>(int id)
                 where PKEntity : EntityObject
                 where FKEntity : EntityObject
             {
                 int changedCount = ;
                 using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                 {
                     try
                     {
                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                         {
                             //根據軟件Id建立EntityKey對象
                             EntityKey entityKey = new EntityKey(BasicArchitectureEntities + typeof(PKEntity)Name Id id)
                             //根據EntityKey查找對應對象
                             PKEntity objResult = contextGetObjectByKey(entityKey) as PKEntity;
                             //根據FKEntity加載導航屬性
                             PropertyInfo propertyInfo = typeof(PKEntity)GetProperty(typeof(FKEntity)Name)
                             EntityCollection<FKEntity> FKEntityList = propertyInfoGetValue(objResult null)
                                 as EntityCollection<FKEntity>;
   
                             if (FKEntityList != null)
                                 FKEntityListLoad()
   
                             if (objResult != null)
                                 contextDeleteObject(objResult)
                             changedCount = contextSaveChanges()
   
                             if (changedCount >
                                 contextAcceptAllChanges()
   
                             scopeComplete()
                         }
                     }
                     catch (Exception ex)
                     { …… }
                 }
                 return changedCount;
             }


    通過下面的方法也可根據輸入的委托predicate批量刪除有關的數據
             public int Delete<T>(Func<Tbool> predicate) where T: EntityObject
             {
                 int changedCount = ;
                 using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                 {
                     try
                     {
                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                         {
                             //根據輸入的委托查找數據
                             var list = contextCreateObjectSet<T>()Where(predicate)
                             //若存在數據刪除有關數據
                             if (listCount() >
                                 foreach (var obj in list)
                                     contextDeleteObject(obj)
   
                             changedCount = contextSaveChanges()
                             if (changedCount >
                                 contextAcceptAllChanges()
   
                             scopeComplete()
                         }
                     }
                     catch (Exception ex)
                     { …… }
                 }
                 return changedCount;
             }
    與前面的例子相同當使用 Delete<Company>(x=>xId==) 方法刪除 Company 對象時由於導航屬性 Position 處於延遲加載的狀態以致系統無法實現同步刪除從而令數據出現邏輯性的錯誤
    此時使用類似的方法利用 RelatedEndLoad() 把導航屬性提前加入到上下文中再刪除Company對象時系統就可以把對應 Position 對象一並刪除
             public int Delete<PKEntity FKEntity>(Func<PKEntitybool> predicate)
                 where PKEntity : EntityObject
                 where FKEntity : EntityObject
             {
                 int changedCount = ;
                 using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                 {
                     try
                     {
                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                         {
                             //根據輸入的委托查找數據
                             var list = contextCreateObjectSet<PKEntity>()Where(predicate)
                             //若數目大於刪除有關數據
                             if (listCount() >
                             {
                                 foreach (var obj in list)
                                 {
                                     //在刪除前加載其導航屬性
                                     PropertyInfo propertyInfo = typeof(PKEntity)GetProperty(typeof(FKEntity)Name)
                                     EntityCollection<FKEntity> FKEntityList = propertyInfoGetValue(obj null)
                                         as EntityCollection<FKEntity>;
                                     if (FKEntityListCount >
                                         FKEntityListLoad()
   
                                     contextDeleteObject(obj)
                                 }
                             }
   
                             changedCount = contextSaveChanges()
   
                             if (changedCount >
                                 contextAcceptAllChanges()
   
                             scopeComplete()
                         }
                     }
                     catch (Exception ex)
                     { …… }
                 }
                 return changedCount;
             }
    此時使用Delete<CompanyPosition>(x=>xId==這樣就可以把Company對象和相關的Position對象同時刪除
    Update
    ObjectContext 中存在方法 ObjectContextApplyCurrentValues<TEntity> 和 ObjectContextApplyOriginalValues<TEntity>用於把將標量值從實體復制到 ObjectContext 中具有相同主鍵的對象集中
    注意在調用此方法前必須把實體預先加載到當前上下文當中要不然系統將會顯示  objectstatemanager 無法跟蹤具有相同鍵的多個對象 的錯誤
    由於DAL層的對象大部分使用單體模式進行開發而BaseCommand是一個共用對象在共同操作時CreateDeleteRead 等操作一般不會對實體造成邏輯性的影響但如果有多個實體同時調用 Update 操作就有可能對實體造成邏輯性影響為了避免這一事件的發生此處使用方法鎖定的模式以 lock(object) 鎖定某一對象以確保在同一時間內只會對一個實體進行更新
    首先通過反射方式獲取對象的Id然後通過 ObjectContextGetObjectByKey(entityKey) 方法把實體加載到當前上下文當中最後利用 ObjectContextApplyCurrentValues<TEntity> 方法把新加入的實體的屬性復制當前上下文
         public class BaseCommand
         {
             private object o = new object()
   
             public int Update<T>(T entity) where T : EntityObject
             {
                 lock (o)
                 {
                     int changedCount = ;
                     Type type = typeof(T)
   
                     using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                     {
                         try
                         {
                             using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                             {
                                 //獲取實體的Id屬性
                                 PropertyInfo property = typeGetProperty(Id
                                 object id = propertyGetValue(entity null)
                                 //根據Id獲取上下文中的對應實體
                                 EntityKey entityKey = new EntityKey(BasicArchitectureEntities
                                       + typeName Id id)
                                 var objResult = contextGetObjectByKey(entityKey)
                                 //更新實體屬性
                                 if (objResult != null)
                                     contextApplyCurrentValues<T>(typeName entity)
   
                                 changedCount = contextSaveChanges()
                                 if (changedCount >
                                     contextAcceptAllChanges()
   
                                 scopeComplete()
                             }
                         }
                         catch (Exception ex)
                         { … }
                     }
                     return changedCount;
                 }
             }
         }
    在一對多多對一關系時也可以使用以下方法進行導航屬性的同步更新首先通過反射獲取主實體的主鍵Id然後建立EntityKey對象再通過ObjectContextGetObjectByKey(entityKey)方法在當前上下文當中獲取此實體最後通過 ObjectContextApplyCurrentValues<TEntity> 方法把新加入的實體的屬性復制當前上下文


    下一步就是對導航屬性進行更新首先通過反射獲取外鍵屬性然後對一對多多對一的關系進行分別處理在一對多關系時把導航屬性轉換成EntityCollection<T>對象集合然後通過 ObjectContextApplyCurrentValues<TEntity> 方法對集合中的每個對象進行逐個更新
    在多對一關系時直接把導航屬性轉換成T類型的對象進行更新
             public int Update<T T>(T entity)
                 where T : EntityObject
                 where T : EntityObject
             {
                 lock (o)
                 {
                     int changedCount = ;
                     Type typeT = typeof(T
                     Type typeT = typeof(T
   
                     using (TransactionScope scope = new TransactionScope(TransactionScopeOptionRequired))
                     {
                         try
                         {
                             using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                             {
                                 PropertyInfo property = typeTGetProperty(Id
                                 object id = propertyGetValue(entity null)
   
                                 //根據軟件Id建立EntityKey對象
                                 EntityKey entityKey = new EntityKey(BasicArchitectureEntities
                                      + typeTName Id id)
                                 //根據EntityKey查找對應對象
                                 T objT = contextGetObjectByKey(entityKey) as T;
                                 //在上下文中更新當前對象
                                 if (objT != null)
                                     contextApplyCurrentValues<T>(typeTName entity)
   
                                 //獲取外鍵屬性
                                 PropertyInfo propertyInfo = typeTGetProperty(typeTName)
   
                                 //在一對多關鍵時更新導航屬性
                                 var TList = propertyInfoGetValue(entity null)
                                        as EntityCollection<T>;
                                 if (TList != null)
                                 {
                                     foreach (var obj in TListToList())
                                     {
                                         var oldEntity = contextGetObjectByKey(objEntityKey)
                                         if (oldEntity != null)
                                             contextApplyCurrentValues<T>(typeTName obj)
                                     }
                                 }
   
                                 //在多對一一對一關系時更新導航屬性
                                 var objT = propertyInfoGetValue(entity null) as T;
                                 if (objT!= null)
                                 {
                                     var oldEntity = contextGetObjectByKey(objTEntityKey)
                                     if (oldEntity != null)
                                         contextApplyCurrentValues<T>(typeTName objT
                                 }
   
                                 changedCount = contextSaveChanges()
                                 if (changedCount >
                                     contextAcceptAllChanges()
   
                                 scopeComplete()
                             }
                         }
                         catch (Exception ex)
                         { …… }
                     }
                     return changedCount;
                 }
             }
    通過此方法無論你要通過Company同步更新Position還是反過來通過Position同步更新Company系統也能正常運行
    Read
    Read 是CRUD中最常見的下面就為大家介紹最通用的幾種方法
    通過Id獲取單個實體
             public T GetObject<T>(int id) where T : EntityObject
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         EntityKey entityKey = new EntityKey(BasicArchitectureEntities
                               + typeof(T)Name Id id)
                         var objResult = contextGetObjectByKey(entityKey)
                         return objResult as T;
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }
    通過輸入的Func<Tbool>委托獲取對象
             public T GetObject<T>(Func<Tbool> predicate) where T : EntityObject
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         var objectSet = contextCreateObjectSet<T>()Where(predicate)
                         if (objectSetCount() >
                             return objectSetFirst()
                         else
                             return null;
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }
    通過輸入的Func<Tbool>委托獲取對象並同時加載單個導航屬性
             public T GetObject<T>(Func<T bool> predicatestring includePath)
                 where T : EntityObject
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         var objectQuery = contextCreateObjectSet<T>()
                             Include(includePath)
                             Where(predicate)
   
                         if (objectQueryCount() >
                             return objectQueryFirst()
                         else
                             return null;
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }
    通過輸入的Func<Tbool>委托獲取對象並同時加載多個導航屬性
             public T GetObject<T>(Func<T bool> predicate string[] includePath)
                  where T : EntityObject
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         var list = contextCreateObjectSet<T>()Where(==
   
                         foreach (var path in includePath)
                             list=listInclude(path)
   
                         var returnValue = listWhere(predicate)ToList()
   
                         if (returnValueCount() >
                             return returnValueFirst()
                         else
                             return null;
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }


    通過輸入的Func<Tbool>委托獲取對象集合
             public IList<T> GetList<T>(Func<Tbool> func) where T:EntityObject
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         ObjectSet<T> objectSet = contextCreateObjectSet<T>()
                         IList<T> list = objectSetWhere(func)ToList()
                         return list;
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }
    通過輸入的Func<Tbool>委托獲取對象集合並同時加入單個導航屬性
             public IList<T> GetList<T>(Func<T bool> funcstring includePath)
                  where T : EntityObject
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         ObjectSet<T> objectSet = contextCreateObjectSet<T>()
                         IList<T> list = objectSetInclude(includePath)Where(func)ToList()
                         return list;
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }
    通過輸入的Func<Tbool>委托獲取對象集合並同時加入多個導航屬性
             public IList<T> GetList<T>(Func<T bool> func string[] includePath)
                 where T : EntityObject
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         var list = contextCreateObjectSet<T>()Where(==
                         foreach (var path in includePath)
                             list = listInclude(path)
                         return listWhere(func)ToList()
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }
    通過原始的SqlCommandText獲取對象集
             public IList<T> GetList<T>(string commandText)
             {
                 try
                 {
                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
                     {
                         IList<T> list = contextExecuteStoreQuery<T>(commandText)ToList()
                         return list;
                     }
                 }
                 catch (Exception ex)
                 {
                     return null;
                 }
             }
    只能完成這一個DAL層的通用類以後您就可在CompanyDALPersonDALPositionDAL …… 等多個類中調用這個通用類輕松地完成各項CRUD的操作
         public class CompanyDAL:ICompanyDAL
         {
             private BaseCommand command = new BaseCommand()
   
             public int AddCompany(Company company)
             {
                 return commandAdd<Company>(company)
             }
   
             public int DeleteCompany(int id)
             {
                 return commandDelete<Company>(id)
             }
   
             public int UpdateComapny(Company company)
             {
                 return commandUpdate<Company>(company)
             }
             ……
         }
    相比起以往的SqlCommand操作Entity Framework更體現出映射的靈活性以往的操作中即使開發出一個通用類CommandText 通常都需要使用手工輸入特別是重復的Update命令操作中往往令人不厭其煩通過Entity Framework可以把CRUD更高度地集中在一個通用類令開發變得更加簡單
    希望本篇文章對您的系統開發有所幫助


From:http://tw.wingwit.com/Article/program/net/201311/13002.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.