.NET
Entity Framework ORM
▌背景
採用Entity Framework在專案中發現的一些小技巧。
PS. 可能視版本不同而需做調整或無法使用之情況。
|
▌環境
l Windows 7 pro
l MS Sql Server 2012
l Visual Studio 2013
updat4 (C#)
l EntityFramework
6.1.1
|
▌實作
▋DbContext Constructor
在加入實體資料模型時,可在DbContext內容,手動加入”注入DbConnection” 的Constructor。 方便從外層注入連線,取代直接由Config設定。
public partial class TmsEntities : DbContext
{
public TmsEntities() : base("name=TmsEntities")
{ }
public TmsEntities(
System.Data.Common.DbConnection
existingConnection,
bool
contextOwnsConnection): base(existingConnection, contextOwnsConnection)
{ }
} |
l
existingConnection
要用於新內容的現有連接。
l contextOwnsConnection
▋Use Partial class to
update DbContext/Models
加入/更新實體資料模型,會讓物件層程式碼重新建立,開發者在上面做的修改都會消失。 好消息是這些類別在建立的時候都是partial class, 所以只要在相同的namespace下再建立另一個partial class來放我們的程式碼即可。
l Model in tt
namespace
Tms.Infra.DAL.Database
{
public partial class TmsCCN
{
public int SN { get; set; }
public string CsvName { get; set; }
public string CsvDate { get; set; }
//...
}
}
|
l Create update codes
for the Model
如下,除了加入Attribute定義,也實作了一些介面。 未來即使修改實體模型,也可直接套用這些程式碼。
namespace
Tms.Infra.DAL.Database
{
[Serializable]
public partial class TmsCCN : ITmsCsv,ICloneable
{
public object Clone()
{
using (Stream objStream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(objStream, this);
objStream.Seek(0, SeekOrigin.Begin);
var cloneObj =
formatter.Deserialize(objStream) as TmsCCN;
return cloneObj;
}
}
}
}
|
▋在DAL層定義一個處理DbContext屬性的父類別Repository
建立父類別後,可讓各子類別DAL的Repository專心於處理CRUD的邏輯。
並可依據不同需求設定DbContext; 例如
/// <summary>
/// DAL父層
/// </summary>
public class BaseDAL
{
/// <summary>
/// Entity framework
model
/// </summary>
protected TmsEntities _tmsEntities = null;
private readonly int? CMD_TIMEOUT = 600; //Default timeout
: 600sec=10min
public BaseDAL(int? timeOut = null)
{
// 建立DbContext
this._tmsEntities = new TmsEntities();
if (timeOut == null){
this._tmsEntities.Database.CommandTimeout
= this.CMD_TIMEOUT;
}
else{
this._tmsEntities.Database.CommandTimeout = timeOut;
}
}
}
|
l 使用Default timeout
public class EntEdiDetailDAL : BaseDAL, IDisposable
{
public EntEdiDetailDAL():base()
{}
}
|
l 或者自訂 timeout
public class EntEdiDetailDAL : InfraDAL, IDisposable
{
public EntEdiDetailDAL():base(timeOut:1200)
{}
}
|
▋Multi-threading
l Entity framework在處理多條執行緒時,無法共用同一個DbContext
錯誤訊息:
“此連接已經在交易中,無法參與其他交易。EntityClient 不支援平行交易。”
所以都必須在每一條Thread都必須使用一個專屬的DbContext。
▋錯誤訊息:”The object was not found in the
ObjectStateManager”
l 此錯誤訊息表示此物件尚未繫結到目前的DbContext。
在此範例中的發生原因是 … 在非同步程式碼中,將另一個DbContext挑出來的Entity,用另一個DbContext做刪除。
public async Task<int> BatchDelete(List<T>
entityList)
{
var cloneContext = this._context.Clone() as TmsEntities;
cloneContext.Configuration.AutoDetectChangesEnabled
= false;
using (var dbContextTransaction = cloneContext.Database.BeginTransaction())
{
try{
cloneContext.TmsCCN.RemoveRange(entityList.AsEnumerable());
cloneContext.SaveChanges();
dbContextTransaction.Commit();
}
catch (Exception ex){
dbContextTransaction.Rollback();
throw;
}
}
cloneContext.Dispose();
cloneContext = null;
return entityList.Count;
}
|
l 解決方式
在執行Remove之前,必須將entity與目前的DbContext繫結,在上面程式碼的灰色地方加入:
foreach (var entity in entityList)
{
if (cloneContext.Entry(entity).State
== EntityState.Detached)
{
cloneContext.TmsCCN.Attach(entity);
}
}
|
▋Reference
沒有留言:
張貼留言