我刚刚开始与PLINQO合作,在我的n层分布式系统中实现存储层和数据层。
数据层由以下层组成:存储库、数据提供程序、数据服务。
存储库层负责从数据库中获取数据并在数据库中设置数据。
数据提供者层是存储库和服务层之间的门。
数据服务层包含所有业务逻辑和规则。
在使用事务时,我在这个体系结构中遇到了问题:我得到了带有错误消息的InvalidOperationException:“不能附加一个已经存在的实体”。经常出现。
原因是存储库层的设计。所有存储库方法中的常见步骤是:
MyDataContext context;
if(InTransaction == true)
context = GetExistingContext();
else
context = GetNewContext();
//Do some database operation for example:
var user = context.User.GetByKey("username");
//Detach from context
user.Detach();
if(InTransaction == false)
context.Dispose();InvalidOperationException发生在InTransaction为true时,我调用两个在同一个实体上操作的方法。因为InTransaction是真的,这两个方法使用相同的数据文本。第一个方法附加实体,最后分离,第二个方法也尝试附加,然后出现异常。
我做错了什么,我怎样才能防止这种情况发生?
谢谢,
柯比
发布于 2011-05-06 15:35:19
我找到了解决问题的办法。
当我试图附加一个已经附加到数据文本的实体时发生了异常,这是因为如果实体已经附加到方法使用的数据上下文,则在Update()方法中没有指示。
当我删除附件时,我没有得到异常,但实体有时没有被udpated (在数据文本是新的情况下)。
因此,我想如何在这两个冲突的情况之间架起桥梁,解决方案是TransactionScope。
我创建了TransactionScope类,它允许方法加入并离开作用域。
Join()方法创建新的datacontext并初始化使用计数器,因此对该方法的任何连续调用都会将此计数器增加一个。
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");
}
}
}https://stackoverflow.com/questions/5903748
复制相似问题