У меня есть программа на C#, которая должна выполнять группу массовых обновлений (20k +) в таблице SQL Server. Поскольку другие пользователи могут обновлять эти записи по одному через веб-сайт интрасети, нам необходимо создать программу C# с возможностью блокировки таблицы. После того, как таблица заблокирована, чтобы другой пользователь не выполнял никаких изменений/поиска, нам необходимо предварительно запрограммировать запрошенные обновления/вставки.C# метод блокировки таблицы SQL Server
Поскольку мы обрабатываем так много записей, мы не можем использовать TransactionScope
(казалось, это был самый простой способ) из-за того, что наша транзакция завершается обработкой MSDTC service. Нам нужно использовать другой метод.
Основываясь на том, что я читал в Интернете, используя объект SqlTransaction
, казалось лучшим методом, однако я не могу заставить стол блокироваться. Когда программа запускается, и я перехожу через код ниже, я все еще могу выполнять обновления и искать через сайт интрасети.
Мой вопрос двоякий. Я правильно использую 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? На основе опубликованного кода эти обновления независимы. – Paparazzi
Нам нужно заблокировать таблицу, так как мы выполняем проверку и обновляем/вставляем как целую группу отдельно. Мы не хотим, чтобы запись проверялась, а затем пользователь использовал сайт интрасети для внесения изменений, которые заставили бы только что подтвержденную запись внезапно нарушить ограничение или что-то еще. – NealR