w12*_*128 3 .net c# sql sql-server-2008
在C#方法中,我执行以下返回多行的SQL查询:
SELECT [Data], [Version]
FROM [dbo].[Table]
WHERE [Id]=@uniqueId AND [ReferenceId] IS NULL
ORDER BY [Version] Asc
Run Code Online (Sandbox Code Playgroud)
然后我迭代结果并调用一个应该更新表的方法:
while (sqlDataReader.Read())
{
SqlBytes data = sqlDataReader.GetSqlBytes(0);
SqlInt64 version = sqlDataReader.GetSqlInt64(1);
UpdateReference(data, version);
}
UpdateReference(data, version)
{
// do database unrelated stuff with data
UPDATE [dbo].[Table]
SET [dbo].[Table].[ReferenceId]=..., [dbo].[Table].[Data]=...
WHERE [dbo].[Table].[Id]=@uniqueId AND [dbo].[Table].[Version]=@version
}
Run Code Online (Sandbox Code Playgroud)
有一段时间这个工作正常,但突然(在SELECT ... INNER JOIN同一个表上执行一些查询后)停止了.我在第一个SELECT上创建了一个事务范围(在调用的方法中UpdateReference()):
using (TransactionScope scope = new TransactionScope())
SELECT ...
while (sqlDataReader.Read()) ... UpdateReference();
Run Code Online (Sandbox Code Playgroud)
我得到以下异常:
交易已中止.
如果我删除了事务范围,则在调用UPDATE一段时间后会发生超时异常:
超时已过期.操作完成之前经过的超时时间或服务器没有响应.
但这似乎不是SQL Server的问题.同样奇怪的是,对于某些记录,没有这样的问题 - 它们只发生在某些表记录上使用第一个SELECT时.
这是我到目前为止发现的:
一个似乎有用的解决方案(现在?)是将第一个查询的结果存储到列表中,然后在SELECT完成后调用list元素上的update :
List<long> versionList = new List<long>();
List<byte[]> dataList = new List<byte[]>();
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Execute SELECT ...
using (SqlCommand sqlCommand = new SqlCommand(selectStatement, connection))
{
...
using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
SqlBytes data = sqlDataReader.GetSqlBytes(0);
SqlInt64 version = sqlDataReader.GetSqlInt64(1);
// Store result to lists
versionList.Add(version.Value);
dataList.Add((byte[])data.ToSqlBinary(););
}
}
}
}
// Everything works as expected if this loop is placed here; but if it is placed within the above SqlConnection using clause, an exception is thrown:
// "Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configurationfor MSDTC using the Component Services Administrative tool."
for (int i = 0; i < versionList.Count; i++)
{
UpdateReference(dataList[i], versionList[i]);
}
scope.Complete();
}
Run Code Online (Sandbox Code Playgroud)
我不确定这个解决方案是否有用(除了使用更多的内存而不是最佳)或者它可能导致的其他潜在问题.如果能够深入了解这里发生的事情以及如何最好地解决问题,我将不胜感激.
更新1
为清楚起见,这是我解决问题的方法:
在TransactionScope外部执行SELECT,将结果存储到列表中;
迭代这些列表并将其内容提供给UPDATE,后者包含在TransactionScope中
随意批评/改进此解决方案:
Method1()
{
List<long> versionList = new List<long>();
List<byte[]> dataList = new List<byte[]>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Execute SELECT ...
using (SqlCommand sqlCommand = new SqlCommand(selectStatement, connection))
{
...
using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
SqlBytes data = sqlDataReader.GetSqlBytes(0);
SqlInt64 version = sqlDataReader.GetSqlInt64(1);
// Store result to lists
versionList.Add(version.Value);
data.Add((byte[])data.ToSqlBinary());
}
}
}
// Call update
for (int i = 0; i < versionList.Count; i++)
{
UpdateReference(dataList[i], versionList[i]);
}
}
}
UpdateReference(data, version)
{
...
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection = new SqlConnection(this.ConnectionString))
{
connection.Open();
UPDATE [dbo].[Table]
SET [dbo].[Table].[ReferenceId]=..., [dbo].[Table].[Data]=...
WHERE [dbo].[Table].[Id]=... AND [dbo].[Table].[Version]=@version
}
scope.Complete();
}
}
Run Code Online (Sandbox Code Playgroud)
是的,select通常需要锁; 在查询本身期间,为了稳定; 但是如果存在事务(取决于隔离级别),那么在查询整个事务之后这些锁可以保持不变; 特别是键范围锁.当然,同一事务中的代码不会受到这些锁的不利影响.特别重要的是打开创建的连接的确切位置,以及您使用的连接数:
dtcping可以帮助)然而!就个人而言,我怀疑在你的情况下最简单的选择是首先在事务之外进行查询并进入列表(或类似) - 即不是懒惰的假脱机.然后完成工作,并应用任何更新.如果可能的话,我会尽量避免单个事务跨越数百/数千个单独的命令 - 如果你可以批量工作,那将是更好的选择.
| 归档时间: |
|
| 查看次数: |
2228 次 |
| 最近记录: |