【问题标题】:PLINQO and Transactions problemPLINQO 和事务问题
【发布时间】:2011-05-05 20:35:27
【问题描述】:

我刚刚开始使用 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();

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

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

谢谢,

科比

【问题讨论】:

    标签: c# linq-to-sql detach plinqo


    【解决方案1】:

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

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

    【讨论】:

    • 很好地找到了解决方案:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-18
    • 1970-01-01
    • 2011-06-04
    相关资源
    最近更新 更多