博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.NET基础篇——Entity Framework 数据转换层通用类
阅读量:4580 次
发布时间:2019-06-09

本文共 22048 字,大约阅读时间需要 73 分钟。

在实现基础的三层开发的时候,大家时常会在数据层对每个实体进行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 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
(List
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. 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表:

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
(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
FKEntityList = 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
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<Company>(x=>x.Id==2) 方法删除 Company 对象时,由于导航属性 Position 处于延迟加载的状态,以致系统无法实现同步删除,从而令数据出现逻辑性的错误。

此时使用类似的方法,利用 RelatedEnd.Load() 把导航属性提前加入到上下文中,再删除Company对象时,系统就可以把对应 Position 对象一并删除。

1           public int Delete
(Func
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 EntityCollection
FKEntityList = 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<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 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<T2>对象集合,然后通过 ObjectContext.ApplyCurrentValues<TEntity> 方法对集合中的每个对象进行逐个更新。
在多对一关系时,直接把导航属性转换成T2类型的对象进行更新。

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<T,bool>委托获取对象

1         public T GetObject
(Func
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<T,bool>委托获取对象,并同时加载单个导航属性

1         public T GetObject
(Func
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<T,bool>委托获取对象,并同时加载多个导航属性

1         public T GetObject
(Func
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<T,bool>委托获取对象集合

1         public IList
GetList
(Func
func) where T:EntityObject 2 { 3 try 4 { 5 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6 { 7 ObjectSet
objectSet = context.CreateObjectSet
(); 8 IList
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
GetList
(Func
func,string includePath) 2 where T : EntityObject 3 { 4 try 5 { 6 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 7 { 8 ObjectSet
objectSet = context.CreateObjectSet
(); 9 IList
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
GetList
(Func
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 IList
GetList
(string commandText) 2 { 3 try 4 { 5 using (BasicArchitectureEntities context = new BasicArchitectureEntities()) 6 { 7 IList
list = 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更高度地集中在一个通用类,令开发变得更加简单。

希望本篇文章对您的系统开发有所帮助。

对软件架构开发有兴趣的朋友欢迎加入博客园讨论组

对 .NET 开发有兴趣的朋友欢迎加入QQ群: 共同探讨 !

作者:风尘浪子

原创作品,转载时请注明作者及出处

 

转载于:https://www.cnblogs.com/leslies2/archive/2012/05/17/2504673.html

你可能感兴趣的文章
js中盒子模型常用的属性你还记得几个?
查看>>
php获取经纬度
查看>>
CVE-2016-5159 利用脏牛漏洞Linux提权复现
查看>>
01背包-第k优解
查看>>
JS引擎深入分析
查看>>
【题解】Luogu P2319 [HNOI2006]超级英雄
查看>>
【题解】Luogu P4324 [JSOI2016]扭动的回文串
查看>>
1038: 亲和数
查看>>
Lintcode: Topological Sorting
查看>>
布局视口,完美视口,
查看>>
带命令行参数的main函数详解
查看>>
Linux driver 原子操作-管理设备被多个文件(进程)打开
查看>>
Java小记
查看>>
一些通过SAP ABAP代码审查得出的ABAP编程最佳实践
查看>>
SAP Cloud for Customer(C4C)和微信集成系列教程
查看>>
最简单的dockerfile使用教程 - 创建一个支持SSL的Nginx镜像
查看>>
读史——秦历史概况
查看>>
Proxy代理对象是如何调用invoke()方法的.
查看>>
iOS 9之SFSafariViewController
查看>>
Sql Server 游标例子笔记
查看>>