.NET基础篇——Entity Framework 数据转换层通用类

   .NET基础篇——Entity Framework 数据转换层通用类            

.NET基础篇——Entity Framework 数据转换层通用类本文将为您介绍一个对应Entity Framework的 CRUD 通用类,相比起以往的SqlCommand操作,Entity Framework更体现出映射的灵活性。以往的操作中,即使开发出一个通用类,CommandText 通常都需要使用手工输入,特别是重复的Update命令操作中,往往令人不厌其烦。通过Entity Framework可以把CRUD更高度地集中在一个通用类,令开发变得更加简单。

在实现基础的三层开发的时候,大家时常会在数据层对每个实体进行CRUD的操作,其中存在相当多的重复代码。为了减少重复代码的出现,通常都会定义一个共用类,实现相似的操作,下面为大家介绍一下Entity Framework时常用到的通用类。
首先在数据库建立起几个关联表:Person、Company、Position,三个实体之间通过导航属性进行相互引用。

.NET基础篇——Entity Framework 数据转换层通用类

下面为大家分别介绍以泛型实现的 Create、Read、Update、Delete 操作:

1. Create

在ObjectContext类之中,早已经为大家预定了一个Create 的操作 AddObject:

void ObjectContext.AddObject(entitySetName string,object entity)
void ObjectSet

 1          public int Add(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)已相当成熟,它不但可以加入单个实体,也可通过导航属性,一次性加入多个关联实体。

 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同时加入实体对象company与position10              int n=command.Add(position);11  12              Console.ReadKey();13          }

 

若要使用批量插入,只要在AddObject方法前多加一个重复语言即可,在此就不再多作解释了。

 1          public int AddList(ListentityList) 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. Delete

同样地,ObjectContext 类当中也存在方法 ObjectContext.DeleteObject(object entity)用于删除实体。
首先通过输入的参数 id 建立起EntityKey对象,然后在ObjectContext查找此实体,若实体存在则使用ObjectContext.DeleteObject(object entity)方法把此实体删除 。

 1           public int Delete(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表:

.NET基础篇——Entity Framework 数据转换层通用类

Position表:

.NET基础篇——Entity Framework 数据转换层通用类

 

此时使用此 int Delete

RelatedEnd是EntityCollection<TEntity> 、EntityReference的父类,它们是特定实体类型的对象集合,该实体类型表示一对多、多对一、多对多的关系。而RelatedEnd.Load()方法,可以将一个或多个相关对象提前加载到相关实体当中。

首先通过ObjectContext.GetObjectByKey(entityKey)方法找到Company对象,然后利用反射属性PropertyInfo类获取导航属性Position,最后使用RelatedEnd.Load()方法,把导航属性加载到当前上下文中。此时使用Delete

 

 1           public int Delete(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                           EntityCollectionFKEntityList = propertyInfo.GetValue(19                                  objResult, null)  as EntityCollection;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(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().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

 1           public int Delete(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().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                                   EntityCollectionFKEntityList = propertyInfo21                                          .GetValue(obj, null) as EntityCollection;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

 

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 entity) where T : EntityObject 6          { 7              lock (o) 8              { 9                      int changedCount = 0;10                      Type type = typeof(T);11 12                      try13                      {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(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

 1          public int Update(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                      try11                      {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(typeT1.Name, entity);25  26                              //获取外键属性27                              PropertyInfo propertyInfo = typeT1.GetProperty(typeT2.Name);28  29                              //在一对多关键时更新导航属性30                              var T2List = propertyInfo.GetValue(entity, null) 
31                                     as EntityCollection;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(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(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(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

 1         public T GetObject(Func<T,bool> predicate) where T : EntityObject 2         { 3             try 4             { 5                 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6                 { 7                     var objectSet = context.CreateObjectSet().Where(predicate); 8                     if (objectSet.Count() > 0) 9                         return objectSet.First();10                     else11                         return null;12                 }13             }14             catch (Exception ex)15             {16                 return null;17             }18         }

 

4.3通过输入的Func

 1         public T GetObject(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() 9                         .Include(includePath)10                         .Where(predicate);11 12                     if (objectQuery.Count() > 0)13                         return objectQuery.First();14                     else15                         return null;16                 }17             }18             catch (Exception ex)19             {20                 return null;21             }22         }

 

4.4通过输入的Func

 1         public T GetObject(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().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                     else18                         return null;19                 }20             }21             catch (Exception ex)22             {23                 return null;24             }25         }

 

4.5 通过输入的Func

 1         public IListGetList(Func<T,bool> func) where T:EntityObject 2         { 3             try 4             { 5                 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6                 { 7                     ObjectSetobjectSet = context.CreateObjectSet(); 8                     IListlist = objectSet.Where(func).ToList(); 9                     return list;10                 }11             }12             catch (Exception ex)13             {14                 return null;15             }16         }

 

4.6通过输入的Func

 1         public IListGetList(Func<T, bool> func,string includePath) 2              where T : EntityObject 3         { 4             try 5             { 6                 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7                 { 8                     ObjectSetobjectSet = context.CreateObjectSet(); 9                     IListlist = objectSet.Include(includePath).Where(func).ToList();10                     return list;11                 }12             }13             catch (Exception ex)14             {15                 return null;16             }17         }

 

4.7通过输入的Func

 1         public IListGetList(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().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 IListGetList(string commandText) 2         { 3             try 4             { 5                 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6                 { 7                     IListlist = context.ExecuteStoreQuery(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); 8         } 9 10         public int DeleteCompany(int id)11         {12             return command.Delete(id);13         }14 15         public int UpdateComapny(Company company)16         {17             return command.Update(company);18         }19         .............20     }

相比起以往的SqlCommand操作,Entity Framework更体现出映射的灵活性。以往的操作中,即使开发出一个通用类,CommandText 通常都需要使用手工输入,特别是重复的Update命令操作中,往往令人不厌其烦。通过Entity Framework可以把CRUD更高度地集中在一个通用类,令开发变得更加简单。
希望本篇文章对您的系统开发有所帮助。

上一篇:C# DataTable转换为Entity(反射&&泛型)


下一篇:cat正常,cat重定向到文件可能是乱码;解决办法