在实现基础的三层开发的时候大家时常会在数据层对每个实体进行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更高度地集中在一个通用类令开发变得更加简单
希望本篇文章对您的系统开发有所帮助