首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PLINQO与交易问题

PLINQO与交易问题
EN

Stack Overflow用户
提问于 2011-05-05 20:35:27
回答 1查看 365关注 0票数 1

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

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

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

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

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

在使用事务时,我在这个体系结构中遇到了问题:我得到了带有错误消息的InvalidOperationException“不能附加一个已经存在的实体”。经常出现。

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

代码语言:javascript
复制
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是真的,这两个方法使用相同的数据文本。第一个方法附加实体,最后分离,第二个方法也尝试附加,然后出现异常。

我做错了什么,我怎样才能防止这种情况发生?

谢谢,

柯比

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-05-06 15:35:19

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

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

当我删除附件时,我没有得到异常,但实体有时没有被udpated (在数据文本是新的情况下)。

因此,我想如何在这两个冲突的情况之间架起桥梁,解决方案是TransactionScope。

我创建了TransactionScope类,它允许方法加入并离开作用域。

Join()方法创建新的datacontext并初始化使用计数器,因此对该方法的任何连续调用都会将此计数器增加一个。

Leave()方法减少使用计数器,当它达到零时,数据文本被释放。

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

我将需要数据上下文的方法从问题中描述的方法更改为:

代码语言:javascript
复制
_myTranscationScope.Join();

try
{
  var context = _myTransactionScope.Context;

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

}
finally
{
    _myTranscationScope.Leave();
}

此外,我重写了数据文本的Dispose方法,并将实体的分离移到那里,而不是在每个方法中这样做。

我需要确保的是,我有正确的事务范围,并确保每个调用加入也调用离开(即使在例外)。

这使得我的代码能够很好地处理所有场景(包括单个数据库操作、复杂事务、处理序列化对象)。

下面是TransactionScope类的代码(我删除了依赖于我正在处理的项目的行代码,但它仍然是完全工作的代码):

代码语言:javascript
复制
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");
        }

    }
 }
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5903748

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档