我找到了解决我的问题的方法。
当我尝试附加已附加到数据上下文的实体时发生异常,这是因为如果实体已附加到该方法使用的数据上下文,我的 Update() 方法中没有指示。
当我删除附件时,我没有得到异常,但实体有时没有更新(如果数据上下文是新的)。
所以我想如何在这两种冲突情况之间架起桥梁,解决方案是 TransactionScope。
我创建了允许方法加入和离开范围的 TransactionScope 类。
Join() 方法创建新的数据上下文并初始化使用计数器,因此对该方法的任何连续调用都会将此计数器加一。
Leave() 方法减少使用计数器,当它达到零时,数据上下文被释放。
还有返回数据上下文的属性(而不是尝试获取自己的上下文的每个方法)。
我将需要数据上下文的方法从我的问题中描述的内容更改为:
_myTranscationScope.Join();
try
{
var context = _myTransactionScope.Context;
//Do some database operation for example:
context.User.GetByKey("username");
}
finally
{
_myTranscationScope.Leave();
}
此外,我覆盖了数据上下文的 Dispose 方法,并将实体的分离移到了那里,而不是在每个方法中都这样做。
我需要确保的是我有正确的事务范围,并确保每个加入的调用也有离开的调用(即使在例外情况下)
现在,这使我的代码在所有场景(包括单个数据库操作、复杂事务、使用序列化对象)中都能完美运行。
这是 TransactionScope 类的代码(我删除了取决于我正在处理的项目的行代码,但它仍然是完整的工作代码):
using System;
using CSG.Games.Data.SqlRepository.Model;
namespace CSG.Games.Data.SqlRepository
{
/// <summary>
/// Defines a transaction scope
/// </summary>
public class TransactionScope :IDisposable
{
private int _contextUsageCounter; // the number of usages in the context
private readonly object _contextLocker; // to make access to _context thread safe
private bool _disposed; // Indicates if the object is disposed
internal TransactionScope()
{
Context = null;
_contextLocker = new object();
_contextUsageCounter = 0;
_disposed = false;
}
internal MainDataContext Context { get; private set; }
internal void Join()
{
// block the access to the context
lock (_contextLocker)
{
CheckDisposed();
// Increment the context usage counter
_contextUsageCounter++;
// If there is no context, create new
if (Context == null)
Context = new MainDataContext();
}
}
internal void Leave()
{
// block the access to the context
lock (_contextLocker)
{
CheckDisposed();
// If no one using the context, leave...
if(_contextUsageCounter == 0)
return;
// Decrement the context usage counter
_contextUsageCounter--;
// If the context is in use, leave...
if (_contextUsageCounter > 0)
return;
// If the context can be disposed, dispose it
if (Context.Transaction != null)
Context.Dispose();
// Reset the context of this scope becuase the transaction scope ended
Context = null;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
lock (_contextLocker)
{
if (_disposed) return;
if (disposing)
{
if (Context != null && Context.Transaction != null)
Context.Dispose();
_disposed = true;
}
}
}
private void CheckDisposed()
{
if (_disposed)
throw new ObjectDisposedException("The TransactionScope is disposed");
}
}
}