【发布时间】:2014-02-11 21:02:42
【问题描述】:
我有一个 C# 程序需要对 SQL Server 表执行一组大规模更新 (20k+)。由于其他用户可以通过内网网站一次更新这些记录,因此我们需要构建具有锁定表功能的 C# 程序。一旦表格被锁定以防止其他用户进行任何更改/搜索,我们将需要执行请求的更新/插入。
由于我们要处理这么多记录,我们不能使用TransactionScope(起初似乎是最简单的方法),因为我们的事务最终由MSDTC service 处理。我们需要使用另一种方法。
根据我在互联网上阅读的内容,使用SqlTransaction 对象似乎是最好的方法,但是我无法锁定表。当程序运行并逐步执行下面的代码时,我仍然能够通过 Intranet 站点执行更新和搜索。
我的问题是双重的。我是否正确使用了SqlTransaction?如果是这样(或者即使不是),是否有更好的方法来获取允许当前运行的程序搜索和执行更新的表锁?
我希望在程序执行下面的代码时锁定表格。
C#
SqlConnection dbConnection = new SqlConnection(dbConn);
dbConnection.Open();
using (SqlTransaction transaction = dbConnection.BeginTransaction(IsolationLevel.Serializable))
{
//Instantiate validation object with zip and channel values
_allRecords = GetRecords();
validation = new Validation();
validation.SetLists(_allRecords);
while (_reader.Read())
{
try
{
record = new ZipCodeTerritory();
_errorMsg = string.Empty;
//Convert row to ZipCodeTerritory type
record.ChannelCode = _reader[0].ToString();
record.DrmTerrDesc = _reader[1].ToString();
record.IndDistrnId = _reader[2].ToString();
record.StateCode = _reader[3].ToString().Trim();
record.ZipCode = _reader[4].ToString().Trim();
record.LastUpdateId = _reader[7].ToString();
record.ErrorCodes = _reader[8].ToString();
record.Status = _reader[9].ToString();
record.LastUpdateDate = DateTime.Now;
//Handle DateTime types separetly
DateTime value = new DateTime();
if (DateTime.TryParse(_reader[5].ToString(), out value))
{
record.EndDate = Convert.ToDateTime(_reader[5].ToString());
}
else
{
_errorMsg += "Invalid End Date; ";
}
if (DateTime.TryParse(_reader[6].ToString(), out value))
{
record.EffectiveDate = Convert.ToDateTime(_reader[6].ToString());
}
else
{
_errorMsg += "Invalid Effective Date; ";
}
//Do not process if we're missing LastUpdateId
if (string.IsNullOrEmpty(record.LastUpdateId))
{
_errorMsg += "Missing last update Id; ";
}
//Make sure primary key is valid
if (_reader[10] != DBNull.Value)
{
int id = 0;
if (int.TryParse(_reader[10].ToString(), out id))
{
record.Id = id;
}
else
{
_errorMsg += "Invalid Id; ";
}
}
//Validate business rules if data is properly formatted
if (string.IsNullOrWhiteSpace(_errorMsg))
{
_errorMsg = validation.ValidateZipCode(record);
}
//Skip record if any errors found
if (!string.IsNullOrWhiteSpace(_errorMsg))
{
_issues++;
//Convert to ZipCodeError type in case we have data/formatting errors
_errors.Add(new ZipCodeError(_reader), _errorMsg);
continue;
}
else if (flag)
{
//Separate updates to appropriate list
SendToUpdates(record);
}
}
catch (Exception ex)
{
_errors.Add(new ZipCodeError(_reader), "Job crashed reading this record, please review all columns.");
_issues++;
}
}//End while
//Updates occur in one of three methods below. If I step through the code,
//and stop the program here, before I enter any of the methods, and then
//make updates to the same records via our intranet site the changes
//made on the site go through. No table locking has occured at this point.
if (flag)
{
if (_insertList.Count > 0)
{
Updates.Insert(_insertList, _errors);
}
if (_updateList.Count > 0)
{
_updates = Updates.Update(_updateList, _errors);
_issues += _updateList.Count - _updates;
}
if (_autotermList.Count > 0)
{
//_autotermed = Updates.Update(_autotermList, _errors);
_autotermed = Updates.UpdateWithReporting(_autotermList, _errors);
_issues += _autotermList.Count - _autotermed;
}
}
transaction.Commit();
}
【问题讨论】:
-
Updates.Insert 和 Updates.Update 有什么作用?您可能需要发布该代码。他们是否使用不同的连接?在任何一种情况下,您都在执行 BeginTransaction,但没有将该事务设置为执行更新的 Command 对象。此外,您永远不会调用 Transaction.Commit();要检查这一点,在代码运行循环时,转到 SSMS 并选择 @@trancount。如果为 0,则不使用任何事务。
-
正如大卫所说,事务没有分配给任何东西。它什么也没做。为什么需要锁定整个表进行 20+ K 更新?根据发布的代码,这些更新是独立的。
-
我们需要锁定表,因为我们作为一个整体单独进行验证和更新/插入。我们不希望验证记录,然后用户使用 Intranet 站点进行更改,这将导致我们刚刚验证的记录突然违反约束或其他内容。
-
所以可以锁定整个表,验证每一行,然后解锁表,然后让用户进行违反约束的更改?
-
不,这是我们要避免的。我们想在表被锁定时验证并执行更新/插入。
标签: c# sql .net sql-server locking