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

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

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

1. Create

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

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

复制代码
 1         public int Add<T>(T entity) where T : EntityObject
 2         {
 3             int changedCount = 0;
 4             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
 5             {
 6                 try
 7                 {
 8                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
 9                     {
10                         context.AddObject(typeof(T).Name, entity);
11                         changedCount = context.SaveChanges();
12                         if (changedCount > 0)
13                             context.AcceptAllChanges();
14                         scope.Complete();
15                     }
16                 }
17                 catch (Exception ex)
18                 { ........ }
19             }
20             return changedCount;
21         }
复制代码

从下面的测试可以看到,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" ,Address="Beijing",Telephone="010-87654321"};
 6             Position position = new Position() { PositionName = "Project Manager", Salary = 15000.00, Company = company };
 7             //通过Add<T>同时加入实体对象company与position
 8             int n=command.Add<Position>(position);
 9 
10             Console.ReadKey();
11         }
复制代码

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

复制代码
 1         public int AddList<T>(List<T> entityList) where T : EntityObject
 2         {
 3             int changedCount = 0;
 4             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
 5             {
 6                 try
 7                 {
 8                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
 9                     {
10                         foreach (T entity in entityList)
11                             context.AddObject(typeof(T).Name, entity);
12                         changedCount = context.SaveChanges();
13                         if (changedCount > 0)
14                             context.AcceptAllChanges();
15                         scope.Complete();
16                     }
17                 }
18                 catch (Exception ex)
19                 { ....... }
20             }
21             return changedCount;
22         }
复制代码

2. Delete

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

复制代码
 1         public int Delete<T>(int id) where T : EntityObject
 2         {
 3             int changedCount = 0;
 4             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
 5             {
 6                 try
 7                 {
 8                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
 9                     {
10                         //建立EntityKey对象
11                         EntityKey entityKey = new EntityKey("BasicArchitectureEntities." + typeof(T).Name, "Id", id);
12                         //通过EntityKey找到实体
13                         var objResult = context.GetObjectByKey(entityKey);
14                         //若实体存在则删除实体
15                         if (objResult != null)
16                             context.DeleteObject(objResult);
17                         changedCount = context.SaveChanges();
18                         if (changedCount > 0)
19                             context.AcceptAllChanges();
20                         
21                         scope.Complete();
22                     }
23                 }
24                 catch (Exception ex)
25                 { ...... }
26             }
27             return changedCount;
28         }
复制代码

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的父类,它们是特定实体类型的对象集合,该实体类型表示一对多、多对一、多对多的关系。而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             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
 7             {
 8                 try
 9                 {
10                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
11                     {
12                         //根据软件Id建立EntityKey对象
13                         EntityKey entityKey = new EntityKey("BasicArchitectureEntities." + typeof(PKEntity).Name, "Id", id);
14                         //根据EntityKey查找对应对象
15                         PKEntity objResult = context.GetObjectByKey(entityKey) as PKEntity;
16                         //根据FKEntity加载导航属性
17                         PropertyInfo propertyInfo = typeof(PKEntity).GetProperty(typeof(FKEntity).Name);
18                         EntityCollection<FKEntity> FKEntityList = propertyInfo.GetValue(objResult, null)
19                             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                         scope.Complete();
32                     }
33                 }
34                 catch (Exception ex)
35                 { ........ }
36             }
37             return changedCount;
38         }
复制代码

通过下面的方法也可根据输入的委托predicate,批量删除有关的数据。

复制代码
 1         public int Delete<T>(Func<T,bool> predicate) where T: EntityObject
 2         {
 3             int changedCount = 0;
 4             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
 5             {
 6                 try
 7                 {
 8                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
 9                     {
10                         //根据输入的委托查找数据
11                         var list = context.CreateObjectSet<T>().Where(predicate);
12                         //若存在数据,删除有关数据
13                         if (list.Count() > 0)
14                             foreach (var obj in list)
15                                 context.DeleteObject(obj);
16 
17                         changedCount = context.SaveChanges();
18                         if (changedCount > 0)
19                             context.AcceptAllChanges();
20 
21                         scope.Complete();
22                     }
23                 }
24                 catch (Exception ex)
25                 { ...... }
26             }
27             return changedCount;
28         }
复制代码

与前面的例子相同,当使用 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             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
 7             {
 8                 try
 9                 {
10                     using (BasicArchitectureEntities context = new BasicArchitectureEntities())
11                     {
12                         //根据输入的委托查找数据
13                         var list = context.CreateObjectSet<PKEntity>().Where(predicate);
14                         //若数目大于0,删除有关数据
15                         if (list.Count() > 0)
16                         {
17                             foreach (var obj in list)
18                             {
19                                 //在删除前加载其导航属性
20                                 PropertyInfo propertyInfo = typeof(PKEntity).GetProperty(typeof(FKEntity).Name);
21                                 EntityCollection<FKEntity> FKEntityList = propertyInfo.GetValue(obj, null) 
22                                     as EntityCollection<FKEntity>;
23                                 if (FKEntityList.Count > 0)
24                                     FKEntityList.Load();
25 
26                                 context.DeleteObject(obj);
27                             }
28                         }
29 
30                         changedCount = context.SaveChanges();
31 
32                         if (changedCount > 0)
33                             context.AcceptAllChanges();
34 
35                         scope.Complete();
36                     }
37                 }
38                 catch (Exception ex)
39                 { ....... }
40             }
41             return changedCount;
42         }
复制代码

此时使用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                 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
13                 {
14                     try
15                     {
16                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
17                         {
18                             //获取实体的Id属性
19                             PropertyInfo property = type.GetProperty("Id");
20                             object id = property.GetValue(entity, null);
21                             //根据Id获取上下文中的对应实体
22                             EntityKey entityKey = new EntityKey("BasicArchitectureEntities." 
23                                   + type.Name, "Id", id);
24                             var objResult = context.GetObjectByKey(entityKey);
25                             //更新实体属性
26                             if (objResult != null)
27                                 context.ApplyCurrentValues<T>(type.Name, entity);
28 
29                             changedCount = context.SaveChanges();
30                             if (changedCount > 0)
31                                 context.AcceptAllChanges();
32 
33                             scope.Complete();
34                         }
35                     }
36                     catch (Exception ex)
37                     { ... }
38                 }
39                 return changedCount;
40             }
41         }
42     }
复制代码

在一对多,多对一关系时,也可以使用以下方法进行导航属性的同步更新。首先通过反射获取主实体的主键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 
11                 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
12                 {
13                     try
14                     {
15                         using (BasicArchitectureEntities context = new BasicArchitectureEntities())
16                         {
17                             PropertyInfo property = typeT1.GetProperty("Id");
18                             object id = property.GetValue(entity, null);
19 
20                             //根据软件Id建立EntityKey对象
21                             EntityKey entityKey = new EntityKey("BasicArchitectureEntities." 
22                                  + typeT1.Name, "Id", id);
23                             //根据EntityKey查找对应对象
24                             T1 objT1 = context.GetObjectByKey(entityKey) as T1;
25                             //在上下文中更新当前对象
26                             if (objT1 != null)
27                                 context.ApplyCurrentValues<T1>(typeT1.Name, entity);
28 
29                             //获取外键属性
30                             PropertyInfo propertyInfo = typeT1.GetProperty(typeT2.Name);
31 
32                             //在一对多关键时更新导航属性
33                             var T2List = propertyInfo.GetValue(entity, null) 
34                                    as EntityCollection<T2>;
35                             if (T2List != null)
36                             {
37                                 foreach (var obj in T2List.ToList())
38                                 {
39                                     var oldEntity = context.GetObjectByKey(obj.EntityKey);
40                                     if (oldEntity != null)
41                                         context.ApplyCurrentValues<T2>(typeT2.Name, obj);
42                                 }
43                             }
44 
45                             //在多对一,一对一关系时更新导航属性
46                             var objT2 = propertyInfo.GetValue(entity, null) as T2;
47                             if (objT2!= null)
48                             {
49                                 var oldEntity = context.GetObjectByKey(objT2.EntityKey);
50                                 if (oldEntity != null)
51                                     context.ApplyCurrentValues<T2>(typeT2.Name, objT2);
52                             }
53 
54                             changedCount = context.SaveChanges();
55                             if (changedCount > 0)
56                                 context.AcceptAllChanges();
57 
58                             scope.Complete();
59                         }
60                     }
61                     catch (Exception ex)
62                     { ...... }
63                 }
64                 return changedCount;
65             }
66         }
复制代码

通过此方法,无论你要通过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更高度地集中在一个通用类,令开发变得更加简单。
希望本篇文章对您的系统开发有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值