在实现基础的三层开发的时候,大家时常会在数据层对每个实体进行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)
public int Add<T>(T entity) where T : EntityObject
{
int changedCount = ;
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
context.AddObject(typeof(T).Name, entity);
changedCount = context.SaveChanges();
if (changedCount > )
context.AcceptAllChanges();
}
}
catch (Exception ex)
{ ........ }
return changedCount;
}
从下面的测试可以看到,ObjectContext.AddObject(entitySetName string,object entity)已相当成熟,它不但可以加入单个实体,也可通过导航属性,一次性加入多个关联实体。
static void Main(string[] args)
{
BaseCommand command = new BaseCommand();
//建立关联实体
Company company = new Company() { CompanyName = "Sun"
,Address="Beijing",Telephone="010-87654321"};
Position position = new Position() { PositionName = "Project Manager"
, Salary = 15000.00, Company = company };
//通过Add<T>同时加入实体对象company与position
int n=command.Add<Position>(position); Console.ReadKey();
}
若要使用批量插入,只要在AddObject方法前多加一个重复语言即可,在此就不再多作解释了。
1 public int AddList<T>(List<T> entityList) where T : EntityObject
{
int changedCount = ;
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
foreach (T entity in entityList)
context.AddObject(typeof(T).Name, entity);
changedCount = context.SaveChanges();
if (changedCount > )
context.AcceptAllChanges();
}
}
catch (Exception ex)
{ ....... }
return changedCount;
}
2. Delete
同样地,ObjectContext 类当中也存在方法 ObjectContext.DeleteObject(object entity)用于删除实体。
首先通过输入的参数 id 建立起EntityKey对象,然后在ObjectContext查找此实体,若实体存在则使用ObjectContext.DeleteObject(object entity)方法把此实体删除 。
1 public int Delete<T>(int id) where T : EntityObject
{
int changedCount = ;
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
//建立EntityKey对象
EntityKey entityKey = new EntityKey(
10 "BasicArchitectureEntities." + typeof(T).Name, "Id", id);
//通过EntityKey找到实体
var objResult = context.GetObjectByKey(entityKey);
//若实体存在则删除实体
if (objResult != null)
context.DeleteObject(objResult);
changedCount = context.SaveChanges();
if (changedCount > )
context.AcceptAllChanges();
}
}
catch (Exception ex)
{ ...... }
return changedCount;
}
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对象
一并删除。
public int Delete<PKEntity, FKEntity>(int id)
where PKEntity : EntityObject
where FKEntity : EntityObject
{
int changedCount = ;
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
//根据软件Id建立EntityKey对象
EntityKey entityKey = new EntityKey(
"BasicArchitectureEntities." + typeof(PKEntity).Name, "Id", id);
//根据EntityKey查找对应对象
PKEntity objResult = context.GetObjectByKey(entityKey) as PKEntity;
//根据FKEntity加载导航属性
PropertyInfo propertyInfo = typeof(PKEntity).GetProperty(
typeof(FKEntity).Name);
EntityCollection<FKEntity> FKEntityList = propertyInfo.GetValue(
objResult, null) as EntityCollection<FKEntity>; if (FKEntityList != null)
FKEntityList.Load(); if (objResult != null)
context.DeleteObject(objResult);
changedCount = context.SaveChanges(); if (changedCount > )
context.AcceptAllChanges();
}
}
catch (Exception ex)
{ ........ }
return changedCount;
}
通过下面的方法也可根据输入的委托predicate,批量删除有关的数据。
public int Delete<T>(Func<T,bool> predicate) where T: EntityObject
{
int changedCount = ;
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
//根据输入的委托查找数据
var list = context.CreateObjectSet<T>().Where(predicate);
//若存在数据,删除有关数据
if (list.Count() > )
foreach (var obj in list)
context.DeleteObject(obj); changedCount = context.SaveChanges();
if (changedCount > )
context.AcceptAllChanges();
}
}
catch (Exception ex)
{ ...... }
return changedCount;
}
与前面的例子相同,当使用 Delete<Company>(x=>x.Id==2) 方法删除 Company 对象时,由于导航属性 Position 处于延迟加载的状态,以致系统无法实现同步删除,从而令数据出现逻辑性的错误。
此时使用类似的方法,利用 RelatedEnd.Load() 把导航属性提前加入到上下文中,再删除Company对象时,系统就可以把对应 Position 对象一并删除。
public int Delete<PKEntity, FKEntity>(Func<PKEntity,bool> predicate)
where PKEntity : EntityObject
where FKEntity : EntityObject
{
int changedCount = ;
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
//根据输入的委托查找数据
var list = context.CreateObjectSet<PKEntity>().Where(predicate);
//若数目大于0,删除有关数据
if (list.Count() > )
{
foreach (var obj in list)
{
//在删除前加载其导航属性
PropertyInfo propertyInfo = typeof(PKEntity)
.GetProperty(typeof(FKEntity).Name);
EntityCollection<FKEntity> FKEntityList = propertyInfo
.GetValue(obj, null) as EntityCollection<FKEntity>;
if (FKEntityList.Count > )
FKEntityList.Load(); context.DeleteObject(obj);
}
}
changedCount = context.SaveChanges(); if (changedCount > )
context.AcceptAllChanges();
}
}
catch (Exception ex)
{ ....... }
return changedCount;
}
此时使用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> 方法,把新加入的实体的属性复制当前上下文。
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); try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
//获取实体的Id属性
PropertyInfo property = type.GetProperty("Id");
object id = property.GetValue(entity, null);
//根据Id获取上下文中的对应实体
EntityKey entityKey = new EntityKey("BasicArchitectureEntities."
+ type.Name, "Id", id);
var objResult = context.GetObjectByKey(entityKey);
//更新实体属性
if (objResult != null)
context.ApplyCurrentValues<T>(type.Name, entity); changedCount = context.SaveChanges();
if (changedCount > )
context.AcceptAllChanges();
}
}
catch (Exception ex)
{ ... }
return changedCount;
}
}
}
在一对多,多对一关系时,也可以使用以下方法进行导航属性的同步更新。首先通过反射获取主实体的主键Id,然后建立EntityKey对象,再通过 ObjectContext.GetObjectByKey(entityKey)方法在当前上下文当中获取此实体,最后通过 ObjectContext.ApplyCurrentValues<TEntity> 方法,把新加入的实体的属性复制当前上下文。
下一步就是对导航属性进行更新,首先通过反射获取外键属性,然后对一对多,多对一的关系进行分别处理。在一对多关系时,把导航属性转换成EntityCollection<T2>对象集合,然后通过 ObjectContext.ApplyCurrentValues<TEntity> 方法对集合中的每个对象进行逐个更新。
在多对一关系时,直接把导航属性转换成T2类型的对象进行更新。
public int Update<T1, T2>(T1 entity)
where T1 : EntityObject
where T2 : EntityObject
{
lock (o)
{
int changedCount = ;
Type typeT1 = typeof(T1);
Type typeT2 = typeof(T2);
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
PropertyInfo property = typeT1.GetProperty("Id");
object id = property.GetValue(entity, null); //根据软件Id建立EntityKey对象
EntityKey entityKey = new EntityKey("BasicArchitectureEntities."
+ typeT1.Name, "Id", id);
//根据EntityKey查找对应对象
T1 objT1 = context.GetObjectByKey(entityKey) as T1;
//在上下文中更新当前对象
if (objT1 != null)
context.ApplyCurrentValues<T1>(typeT1.Name, entity); //获取外键属性
PropertyInfo propertyInfo = typeT1.GetProperty(typeT2.Name); //在一对多关键时更新导航属性
var T2List = propertyInfo.GetValue(entity, null)
as EntityCollection<T2>;
if (T2List != null)
{
foreach (var obj in T2List.ToList())
{
var oldEntity = context.GetObjectByKey(obj.EntityKey);
if (oldEntity != null)
context.ApplyCurrentValues<T2>(typeT2.Name, obj);
}
} //在多对一,一对一关系时更新导航属性
var objT2 = propertyInfo.GetValue(entity, null) as T2;
if (objT2!= null)
{
var oldEntity = context.GetObjectByKey(objT2.EntityKey);
if (oldEntity != null)
context.ApplyCurrentValues<T2>(typeT2.Name, objT2);
} changedCount = context.SaveChanges();
if (changedCount > )
context.AcceptAllChanges();
}
catch (Exception ex)
{ ...... }
return changedCount;
}
}
通过此方法,无论你要通过Company同步更新Position,还是反过来通过Position同步更新Company,系统也能正常运行。
4. Read
Read 是CRUD中最常见的,下面就为大家介绍最通用的几种方法
4.1 通过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 = context.GetObjectByKey(entityKey);
return objResult as T;
}
}
catch (Exception ex)
{
return null;
}
}
4.2 通过输入的Func<T,bool>委托获取对象
public T GetObject<T>(Func<T,bool> predicate) where T : EntityObject
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
var objectSet = context.CreateObjectSet<T>().Where(predicate);
if (objectSet.Count() > )
return objectSet.First();
else
return null;
}
}
catch (Exception ex)
{
return null;
}
}
4.3通过输入的Func<T,bool>委托获取对象,并同时加载单个导航属性
public T GetObject<T>(Func<T, bool> predicate,string includePath)
where T : EntityObject
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
var objectQuery = context.CreateObjectSet<T>()
.Include(includePath)
.Where(predicate); if (objectQuery.Count() > )
return objectQuery.First();
else
return null;
}
}
catch (Exception ex)
{
return null;
}
}
4.4通过输入的Func<T,bool>委托获取对象,并同时加载多个导航属性
public T GetObject<T>(Func<T, bool> predicate, string[] includePath)
where T : EntityObject
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
var list = context.CreateObjectSet<T>().Where("1==1"); foreach (var path in includePath)
list=list.Include(path); var returnValue = list.Where(predicate).ToList(); if (returnValue.Count() > )
return returnValue.First();
else
return null;
}
}
catch (Exception ex)
{
return null;
}
}
4.5 通过输入的Func<T,bool>委托获取对象集合
public IList<T> GetList<T>(Func<T,bool> func) where T:EntityObject
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
ObjectSet<T> objectSet = context.CreateObjectSet<T>();
IList<T> list = objectSet.Where(func).ToList();
return list;
}
}
catch (Exception ex)
{
return null;
}
}
4.6通过输入的Func<T,bool>委托获取对象集合,并同时加入单个导航属性
public IList<T> GetList<T>(Func<T, bool> func,string includePath)
where T : EntityObject
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
ObjectSet<T> objectSet = context.CreateObjectSet<T>();
IList<T> list = objectSet.Include(includePath).Where(func).ToList();
return list;
}
}
catch (Exception ex)
{
return null;
}
}
4.7通过输入的Func<T,bool>委托获取对象集合,并同时加入多个导航属性
public IList<T> GetList<T>(Func<T, bool> func, string[] includePath)
where T : EntityObject
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
var list = context.CreateObjectSet<T>().Where("1==1");
foreach (var path in includePath)
list = list.Include(path);
return list.Where(func).ToList();
}
}
catch (Exception ex)
{
return null;
}
}
4.8 通过原始的SqlCommandText获取对象集
public IList<T> GetList<T>(string commandText)
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
IList<T> list = context.ExecuteStoreQuery<T>(commandText).ToList();
return list;
}
}
catch (Exception ex)
{
return null;
}
}
只能完成这一个DAL层的通用类以后,您就可在CompanyDAL、PersonDAL、PositionDAL ...... 等多个类中调用这个通用类,轻松地完成各项CRUD的操作。
public class CompanyDAL:ICompanyDAL
{
private BaseCommand command = new BaseCommand(); public int AddCompany(Company company)
{
return command.Add<Company>(company);
} public int DeleteCompany(int id)
{
return command.Delete<Company>(id);
} public int UpdateComapny(Company company)
{
return command.Update<Company>(company);
}
.............
}
相比起以往的SqlCommand操作,Entity Framework更体现出映射的灵活性。以往的操作中,即使开发出一个通用类,CommandText 通常都需要使用手工输入,特别是重复的Update命令操作中,往往令人不厌其烦。通过Entity Framework可以把CRUD更高度地集中在一个通用类,令开发变得更加简单。
希望本篇文章对您的系统开发有所帮助。
.NET基础篇——Entity Framework 数据转换层通用类的更多相关文章
-
第三篇 Entity Framework Plus 之 Query Cache
离上一篇博客,快一周,工作太忙,只能利用休息日来写一些跟大家分享,Entity Framework Plus 组件系列文章,之前已经写过两篇 第一篇 Entity Framework Plus 之 A ...
-
";Entity Framework数据插入性能追踪";读后总结
园友莱布尼茨写了一篇<Entity Framework数据插入性能追踪>的文章,我感觉不错,至少他提出了问题,写了出来,引起了大家的讨论,这就是一个氛围.读完文章+评论,于是我自己也写了个 ...
-
第二篇 Entity Framework Plus 之 Query Future
从性能的角度出发,能够减少 增,删,改,查,跟数据库打交道次数,肯定是对性能会有所提升的(这里单纯是数据库部分). 今天主要怎样减少Entity Framework查询跟数据库打交道的次数,来提高查询 ...
-
Hibernate.基础篇《一》.Hibernate工具类.
Hibernate.基础篇<一>.Hibernate工具类. 话述: Hibernate.基础篇第一篇,前面是代码.后面再加理论&实践. Hibernate使用的版本是:5.x,在 ...
-
第四篇 Entity Framework Plus 之 Batch Operations
用 Entity Framework 进行 增,删,改.都是基于Model进行的,且Model都是有状态追踪的.这样Entity Framework才能正常增,删,改. 有时候,要根据某个字段,批量 ...
-
第一篇 Entity Framework Plus 之 Audit
一般系统会有登陆日志,操作日志,异常日志,已经满足大部分的需求了.但是有时候,还是需要Audit 审计日志,审计日志,主要针对数据增,改,删操作数据变化的记录,主要是对数据变化的一个追踪过程.其中主要 ...
-
asp.net mvc常用的数据注解和验证以及entity framework数据映射
终于有时间整理一下asp.net mvc 和 entity framework 方面的素材了. 闲话少说,步入正题: 下面是model层的管理员信息表,也是大伙比较常用到的,看看下面的代码大伙应该不会 ...
-
Entity Framework 数据并发访问错误原因分析与系统架构优化
博客地址 http://blog.csdn.net/foxdave 本文主要记录近两天针对项目发生的数据访问问题的分析研究过程与系统架构优化,我喜欢说通俗的白话,高手轻拍 1. 发现问题 系统新模块上 ...
-
Entity Framework 数据部分更新之Attach &;&;Detach
我们经常会遇到这样的问题:Update一个entity的部分数据时,通常需要new一个新的对象,然后事这新的对象Attach到Context中,代码如下所示: /// <summary> ...
随机推荐
-
selenium 富文本框处理
selenium 富文本框处理, 网上有用API的解决方法1:参见:http://blog.csdn.net/xc5683/article/details/8963621 群里1位群友的解决方法2:参 ...
-
Atitit. 提升软件开发效率and 开发质量---java 实现dsl 4gl 的本质and 精髓 O725
Atitit. 提升软件开发效率and 开发质量---java 实现dsl 4gl 的本质and 精髓 O725 1. DSL主要分为三类:外部DSL.内部DSL,以及语言工作台. 1 2. DSL ...
-
vi查找
/pattern<Enter> :向下查找pattern匹配字符串 ?pattern<Enter>:向上查找pattern匹配字符串 使用了查找命令之后,使用如下两个键快速查找 ...
-
HDU 4122 Alice&#39;s mooncake shop (单调队列/线段树)
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4122 题意:好难读懂,读懂了也好难描述,亲们就自己凑合看看题意把 题解:开始计算每个日期到2000/1/ ...
-
js调用wcf 的SOA
jquery 调用wcf 的SOA架构,将三层架构运用到SOA的架构中来 经过前面3天的学习,我想大家应该对SOA的架构有了初步的了解,其实 SOA与三层架构并不冲突,而是三层架构的升级版. 来看下传 ...
-
git fetch
http://www.ruanyifeng.com/blog/2012/07/git.html 流程 默认情况下,git fetch取回所有分支(branch)的更新.如果只想取回特定分支的更新,可以 ...
-
设计模式_代理模式_在SqlSessionTemplate(Spring)中的应用
1.SqlSessionTemplate的构造函数,根据传入的SqlSessionFactory和ExecutorType创建一个Spring管理的SqlSession,并生成SqlSession的动 ...
-
unity2D限制位置的背景移动补偿效果
有时候我们想要背景可以跟随相机移动补偿,但是又不想该背景物体离原来的位置太远,比如我们想要一棵树在一个房子的后面,然后使用相机补偿使其跟随移动,达到3D错觉效果,但是我们又不想该物体偏离房屋太远.假设 ...
-
【*】深入理解redis主从复制原理
1.复制过程 从节点执行 slaveof 命令. 从节点只是保存了 slaveof 命令中主节点的信息,并没有立即发起复制. 从节点内部的定时任务发现有主节点的信息,开始使用 socket 连接主节点 ...
-
C#中配置文件保存的路径
http://www.codeproject.com/Tips/350010/Saving-User-Settings-in-Winform-Application 外网上找的资料 winform提供 ...