多线程linq2sql应用程序TransactionScope的困难

时间:2011-04-22 13:20:34

标签: c# multithreading linq-to-sql transactionscope

我已经创建了一个文件处理服务,它从特定目录中读取和导入xml文件。

该服务启动了几个工作程序,这些工作程序将轮询文件队列以查找新文件,并使用linq2sql进行数据访问。每个workerthread都有自己的datacontext。

正在处理的文件包含多个订单,每个订单包含多个地址(客户/承包商/转包商)

我已经围绕每个文件的处理定义了一个transactioncope。这样我想确保正确处理整个文件,或者在发生异常时回滚整个文件:

        try
        {
            using (var tx = new TransactionScope(TransactionScopeOption.RequiresNew))
            {
                foreach (var order in orders)
                {
                    HandleType1Order(order);
                }
                tx.Complete();
            }
        }
        catch (SqlException ex)
        {
            if (ex.Number == SqlErrorNumbers.Deadlock)
            {
                throw new FileHandlerException("File Caused a Deadlock, retrying later", ex, true);
            }
            else
                throw;
        }

该服务的一个要求是在xml文件中创建或更新找到的地址。所以我创建了一个负责地址管理的地址服务。 xml importfile中的每个订单(在方法HandleType1Order()内)执行以下代码(因此是整个文件的TransactionScope的一部分)。

 using (var tx = new TransactionScope())
            {

                address = GetAddressByReference(number);
                if (address != null) //address is already known
                {
                    Log.Debug("Found address {0} - {1}. Updating...", address.Code, address.Name);
                    UpdateAddress(address, name, number, isContractor, isSubContractor, isCustomer);
                }
                else
                {
                    //address not known, so create it
                    Log.Debug("Address {0} not known, creating address", number);
                    address = CreateAddress(name, number, sourceSystemId, isContractor, isSubContractor,
                                            isCustomer);
                    _addressRepository.Save(address);

                }

                _addressRepository.Flush();
                tx.Complete();
            }

我在这里要做的是创建或更新地址,数字是唯一的。

方法GetAddressByReference(string number)返回已知地址,如果找不到地址,则返回null。

 public virtual Address GetAddressByReference(string reference)
 {
     return _addressRepository.GetAll().SingleOrDefault(a=>a.Code==reference);
 }

当我运行服务时,它会创建多个具有相同编号的地址。方法GetAddressByReference()得到并发调用,并且当第二个线程执行具有相同地址编号的方法时应该返回已知地址,但是它返回null。我的交易边界或隔离级别可能存在问题,但我似乎无法使其发挥作用。

有人能指出我正确的方向吗?非常感谢帮助!!

P.S。我没有遇到死锁并导致回滚的问题,只有在发生死锁时才会重试该文件。


编辑1 线程代码:

        public void Work()
    {
        _isRunning = true;
        while (true)
        {
            ImportFileTask task = _queue.Dequeue(); //dequeue blocks on empty queue               
            if (task == null)
                break; //Shutdown worker when a null task is read from the queue

            IFileImporter importer = null;
            try
            {
                using (new LockFile(task.FilePath).Acquire()) //create a filelock to sync access accross all processes to the file
                {
                    importer = _kernel.Resolve<IFileImporter>();
                    Log.DebugFormat("Processing file {0}", task.FilePath);
                    importer.Import(task.FilePath);
                    Log.DebugFormat("Done Processing file {0}", task.FilePath);
                }
            }
            catch(Exception ex)
            {
                Log.Fatal(
                    "A Fatal exception occured while handling {0} --> {1}".FormatWith(task.FilePath, ex.Message), ex);
            }
            finally
            {
                if (importer != null)
                    _kernel.ReleaseComponent(importer);
            }

        }

        _isRunning = false;
    }

上述方法在我们所有的工作线程中运行。它使用Castle Windsor来解析FileImporter,它具有短暂的生活方式(因此不会在线程中共享)。

1 个答案:

答案 0 :(得分:0)

您没有发布您的线程代码,因此很难说出问题所在。我假设你已经启动了DTC(分布式事务协调器)?

您使用的是ThreadPool吗?您使用的是“锁定”关键字吗?

http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx