我试图在并行线程中读取相同的 SQL Server 文件流,但没有成功。
大多数情况下,我得到以下异常(尽管有时我会遇到其他错误):
System.InvalidOperationException:“进程无法访问指定的文件,因为它已在另一个事务中打开。”
我在互联网上搜索过,只找到了一些帖子,但据我所知,这应该可行。我正在使用 SQL Server 2008 R2。
我已将代码简化为以下内容:主代码打开一个主事务,然后并行运行 2 个线程,每个线程使用一个DependentTransaction
并将 SQL Server 文件流复制到磁盘上的临时文件中。
如果我将 threadCount 更改为 1,则代码有效。
知道为什么这会失败吗?
编码:
class Program
{
private static void Main(string[] args)
{
string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(path);
try
{
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required))
{
TransactionInterop.GetTransmitterPropagationToken(Transaction.Current);
const int threadCount = 2;
var transaction = Transaction.Current;
// Create dependent transactions, one for each thread
var dependentTransactions = Enumerable
.Repeat(transaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete), threadCount)
.ToList();
// Copy the file from the DB to a temporary files, in parallel (each thread will use a different temporary file).
Parallel.For(0, threadCount, i =>
{
using (dependentTransactions[i])
{
CopyFile(path, dependentTransactions[i]);
dependentTransactions[i].Complete();
}
});
transactionScope.Complete();
}
}
finally
{
if (Directory.Exists(path))
Directory.Delete(path, true);
}
}
private static void CopyFile(string path, DependentTransaction dependentTransaction)
{
string tempFilePath = Path.Combine(path, Path.GetRandomFileName());
// Open a transaction scope for the dependent transaction
using (var transactionScope = new TransactionScope(dependentTransaction, TransactionScopeAsyncFlowOption.Enabled))
{
using (Stream stream = GetStream())
{
// Copy the SQL stream to a temporary file
using (var tempFileStream = File.OpenWrite(tempFilePath))
stream.CopyTo(tempFileStream);
}
transactionScope.Complete();
}
}
// Gets a SQL file stream from the DB
private static Stream GetStream()
{
var sqlConnection = new SqlConnection("Integrated Security=true;server=(local);initial catalog=DBName");
var sqlCommand = new SqlCommand {Connection = sqlConnection};
sqlConnection.Open();
sqlCommand.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()";
Object obj = sqlCommand.ExecuteScalar();
byte[] txContext = (byte[])obj;
const string path = "\\\\MyMachineName\\MSSQLSERVER\\v1\\DBName\\dbo\\TableName\\TableName\\FF1444E6-6CD3-4AFF-82BE-9B5FCEB5FC96";
var sqlFileStream = new SqlFileStream(path, txContext, FileAccess.Read, FileOptions.SequentialScan, 0);
return sqlFileStream;
}
}
kk