2011-05-05 94 views
1

我刚开始与PLINQO合作,在我的n层分布式系统中实现存储库层和数据层。PLINQO和交易问题

数据层由下列各层组成:储存库,数据提供者,数据服务

负责从数据库中获取数据,并在数据库中设置的数据存储库层。

数据提供层是存储库和服务层

数据服务层包含所有业务逻辑和规则之间的栅极。

我在使用事务处理时遇到此架构中的问题:我得到InvalidOperationException带有错误消息:“无法附加已存在的实体。很多。

原因是因为存储库层的设计。在所有存储库方法的共同步骤是:当在inTransaction是真实的,我打电话给在同一实体操作的两种方法

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是真的,所以两个方法使用相同的数据上下文。第一种方法附加实体并在最后分离,第二种方法也尝试附加,然后发生异常。

我在做什么错,我该如何防止这种情况发生?

感谢,

Koby

回答

2

我找到了解决我的问题。

当我试图附加一个已经附加到datacontext的实体时,发生了异常,这是因为我的Update()方法中没有指示实体是否已经附加到数据上下文方法使用。

当我删除附件时,我没有得到异常,但实体有时没有udpated(如果datacontext是新的)。

所以我想如何在这两种冲突情况之间架起桥梁,解决方案就是TransactionScope。

我创建了TransactionScope类,允许方法加入和离开范围。

Join()方法创建新的数据上下文并初始化使用计数器,因此对此方法的任何连续调用都会将此计数器加1。

Leave()方法递减使用计数器,当它到达零时,将处理datacontext。

还有一个属性返回数据上下文(而不是每个方法来尝试获取自己的上下文)。

我改变需要从我的问题是什么描述数据上下文的方法:

_myTranscationScope.Join(); 

try 
{ 
    var context = _myTransactionScope.Context; 

    //Do some database operation for example: 
    context.User.GetByKey("username"); 

} 
finally 
{ 
    _myTranscationScope.Leave(); 
} 

另外我overrided在DataContext的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"); 
     } 

    } 
} 
+0

上找到一个解决办法干得好:) – 2011-07-07 17:39:46