1

我刚开始使用 PLINQO 在我的 n 层分布式系统中实现存储库层和数据层。

数据层由以下层组成:存储库、数据提供者、数据服务

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

数据提供者层是存储库和服务层之间的大门

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

使用事务时,我在此体系结构中遇到问题:我收到InvalidOperationException错误消息:“无法附加已存在的实体。” 常常。

原因是因为repository层的设计。所有存储库方法中的常见步骤是:

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();

InTransaction为真并且我正在调用对同一实体进行操作的两个方法时,会发生InvalidOperationException 。因为 InTransaction 为真,这两种方法使用相同的数据上下文。第一种方法附加实体并最终分离,第二种方法也尝试附加,然后发生异常。

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

谢谢,

科比

4

1 回答 1

2

我找到了解决我的问题的方法。

当我尝试附加已附加到数据上下文的实体时发生异常,这是因为如果实体已附加到该方法使用的数据上下文,我的 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");
        }

    }
 }
于 2011-05-06T15:35:19.123 回答