在實現基礎的三層開發的時候大家時常會在數據層對每個實體進行CRUD的操作其中存在相當多的重復代碼為了減少重復代碼的出現通常都會定義一個共用類實現相似的操作下面為大家介紹一下Entity Framework時常用到的通用類
首先在數據庫建立起幾個關聯表PersonCompanyPosition三個實體之間通過導航屬性進行相互引用
![](http://img.educity.cn/img_5/117/2013101909/12506092052.png)
下面為大家分別介紹以泛型實現的 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(TransactionScopeOption
Required))
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
foreach (T entity in entityList)
context
AddObject(typeof(T)
Name
entity)
changedCount = context
SaveChanges()
if (changedCount >
)
context
AcceptAllChanges()
scope
Complete()
}
}
catch (Exception ex)
{ …… }
}
return changedCount;
}
Delete
同樣地
ObjectContext 類當中也存在方法 ObjectContext
DeleteObject(object entity)用於刪除實體
首先通過輸入的參數 id 建立起EntityKey對象
然後在ObjectContext查找此實體
若實體存在則使用ObjectContext
DeleteObject(object entity)方法把此實體刪除
public int Delete<T>(int id) where T : EntityObject
{
int changedCount =
;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption
Required))
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
//建立EntityKey對象
EntityKey entityKey = new EntityKey(
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()
scope
Complete()
}
}
catch (Exception ex)
{ …… }
}
return changedCount;
}
ObjectContext
DeleteObject(object entity)與ObjectContext
AddObject(entitySetName string
object entity)相同
可以通過導航屬性
一次性刪除多個關聯實體
但如果數據庫中存在下面的數據
Company表
Position表
此時使用此 int Delete<Company>(
) 方法刪除Company對象
系統將會報錯
這是由於導航屬性在默認情況下具有延時加載的特性
在系統使用ObjectContext
GetObjectByKey(entityKey)方法加載實體時
它的導航屬性不會馬上加載到上下文當中
而是在調用該導航屬性時
對象才會被加載
因而系統通過ObjectContext
GetObjectByKey(
)獲取Company對象時
對應的Position對象並未被加載到上下文當中
所以當刪除Company對象時
Position對象不能被同步刪除
因而造成邏輯上的錯誤
為解決這一問題
可以利用RelatedEnd
Load()方法提前加載導航屬性
RelatedEnd是EntityCollection<TEntity>
EntityReference的父類
它們是特定實體類型的對象集合
該實體類型表示一對多
多對一
多對多的關系
而RelatedEnd
Load()方法
可以將一個或多個相關對象提前加載到相關實體當中
首先通過ObjectContext
GetObjectByKey(entityKey)方法找到Company對象
然後利用反射屬性PropertyInfo類獲取導航屬性Position
最後使用RelatedEnd
Load()方法
把導航屬性加載到當前上下文中
此時使用Delete<Company
Position>(
)方法刪除Company對象時
系統將能正常運行
並把對應的Position對象一並刪除
public int Delete<PKEntity
FKEntity>(int id)
where PKEntity : EntityObject
where FKEntity : EntityObject
{
int changedCount =
;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption
Required))
{
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()
scope
Complete()
}
}
catch (Exception ex)
{ …… }
}
return changedCount;
}
通過下面的方法也可根據輸入的委托predicate
批量刪除有關的數據
public int Delete<T>(Func<T
bool> predicate) where T: EntityObject
{
int changedCount =
;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption
Required))
{
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()
scope
Complete()
}
}
catch (Exception ex)
{ …… }
}
return changedCount;
}
與前面的例子相同
當使用 Delete<Company>(x=>x
Id==
) 方法刪除 Company 對象時
由於導航屬性 Position 處於延遲加載的狀態
以致系統無法實現同步刪除
從而令數據出現邏輯性的錯誤
此時使用類似的方法
利用 RelatedEnd
Load() 把導航屬性提前加入到上下文中
再刪除Company對象時
系統就可以把對應 Position 對象一並刪除
public int Delete<PKEntity
FKEntity>(Func<PKEntity
bool> predicate)
where PKEntity : EntityObject
where FKEntity : EntityObject
{
int changedCount =
;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption
Required))
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
//根據輸入的委托查找數據
var list = context
CreateObjectSet<PKEntity>()
Where(predicate)
//若數目大於
刪除有關數據
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()
scope
Complete()
}
}
catch (Exception ex)
{ …… }
}
return changedCount;
}
此時使用Delete<Company
Position>(x=>x
Id==
)
這樣就可以把Company對象和相關的Position對象同時刪除
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)
using (TransactionScope scope = new TransactionScope(TransactionScopeOption
Required))
{
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()
scope
Complete()
}
}
catch (Exception ex)
{ … }
}
return changedCount;
}
}
}
在一對多
多對一關系時
也可以使用以下方法進行導航屬性的同步更新
首先通過反射獲取主實體的主鍵Id
然後建立EntityKey對象
再通過ObjectContext
GetObjectByKey(entityKey)方法在當前上下文當中獲取此實體
最後通過 ObjectContext
ApplyCurrentValues<TEntity> 方法
把新加入的實體的屬性復制當前上下文
下一步就是對導航屬性進行更新
首先通過反射獲取外鍵屬性
然後對一對多
多對一的關系進行分別處理
在一對多關系時
把導航屬性轉換成EntityCollection<T
>對象集合
然後通過 ObjectContext
ApplyCurrentValues<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(TransactionScopeOption
Required))
{
try
{
using (BasicArchitectureEntities context = new BasicArchitectureEntities())
{
PropertyInfo property = typeT
GetProperty(
Id
)
object id = property
GetValue(entity
null)
//根據軟件Id建立EntityKey對象
EntityKey entityKey = new EntityKey(
BasicArchitectureEntities
+ typeT
Name
Id
id)
//根據EntityKey查找對應對象
T
objT
= context
GetObjectByKey(entityKey) as T
;
//在上下文中更新當前對象
if (objT
!= null)
context
ApplyCurrentValues<T
>(typeT
Name
entity)
//獲取外鍵屬性
PropertyInfo propertyInfo = typeT
GetProperty(typeT
Name)
//在一對多關鍵時更新導航屬性
var T
List = propertyInfo
GetValue(entity
null)
as EntityCollection<T
>;
if (T
List != null)
{
foreach (var obj in T
List
ToList())
{
var oldEntity = context
GetObjectByKey(obj
EntityKey)
if (oldEntity != null)
context
ApplyCurrentValues<T
>(typeT
Name
obj)
}
}
//在多對一
一對一關系時更新導航屬性
var objT
= propertyInfo
GetValue(entity
null) as T
;
if (objT
!= null)
{
var oldEntity = context
GetObjectByKey(objT
EntityKey)
if (oldEntity != null)
context
ApplyCurrentValues<T
>(typeT
Name
objT
)
}
changedCount = context
SaveChanges()
if (changedCount >
)
context
AcceptAllChanges()
scope
Complete()
}
}
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 = context
GetObjectByKey(entityKey)
return objResult as T;
}
}
catch (Exception ex)
{
return null;
}
}
通過輸入的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;
}
}
通過輸入的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;
}
}
通過輸入的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(
==
)
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;
}
}
通過輸入的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;
}
}
通過輸入的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;
}
}
通過輸入的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(
==
)
foreach (var path in includePath)
list = list
Include(path)
return list
Where(func)
ToList()
}
}
catch (Exception ex)
{
return null;
}
}
通過原始的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更高度地集中在一個通用類
令開發變得更加簡單
希望本篇文章對您的系統開發有所幫助
From:http://tw.wingwit.com/Article/program/net/201311/13002.html