[英]Multithreaded linq2sql applications TransactionScope difficulties
我创建了一个文件处理服务,该服务从特定目录读取和导入xml文件。
该服务启动了几个工作程序,这些工作程序将轮询文件队列以查找新文件,并使用linq2sql进行数据访问。 每个工作线程都有自己的数据上下文。
正在处理的文件包含多个订单,每个订单包含多个地址(客户/承包商/分包商)
我已经围绕每个文件的处理定义了一个transactionscope。 这样,我想确保正确处理整个文件,或者在发生异常时回退整个文件:
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。 我的事务边界或隔离级别可能存在问题,但是我似乎无法使其正常工作。
有人可以指出我正确的方向吗? 帮助非常感谢!
ps我对事务被死锁并导致回滚没有问题,当死锁发生时,将仅重试该文件。
编辑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,该文件具有短暂的生活方式(因此无法跨线程共享)。
您没有发布线程代码,因此很难说出问题所在。 我假设您已经启动了DTC(分布式事务处理协调器)?
您在使用ThreadPool吗? 您是否在使用“锁定”关键字?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.