Hej.
Problem mam dość irytujący i od jakiegoś czasu mocno dokuczający, więc potrzebuję jakiegoś punktu zaczepienia, ze względu na wyczerpanie się moich pomysłów, co jest nie tak.
Przyjmijmy w uproszczeniu, że mój kod wygląda tak (baza MSSQL, .NET 4.0):
bool correct = false;
do
{
SqlTransaction transaction = null;
try
{
// połącz z bazą:
// (...)
transaction = database.conn.BeginTransaction(IsolationLevel.Serializable);
SqlCommand selectCmd = new SqlCommand();
selectCmd.Connection = database.conn;
selectCmd.CommandType = CommandType.Text;
selectCmd.CommandText = @"SELECT TOP 1 id1, id2 FROM ids";
selectCmd.Transaction = transaction;
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = selectCmd;
DataTable dtIds = new DataTable();
da.Fill(dtIds);
int id1 = Int32.Parse(dtIds.Rows[0]["id1"].ToString());
int id2 = Int32.Parse(dtIds.Rows[0]["id2"].ToString());
SqlCommand sqlUpdtIds = new SqlCommand();
sqlUpdtIds.Connection = database.conn;
sqlUpdtIds.CommandType = CommandType.Text;
sqlUpdtIds.CommandText = @"UPDATE ids SET id1 = id1 + 1, id2 = id2 + 1";
sqlUpdtIds.Transaction = transaction;
sqlUpdtIds.ExecuteNonQuery();
// jakieś tam operacja korzystające z powyższych id'ków
SqlBulkCopy bulkCopy = new SqlBulkCopy(database.conn, SqlBulkCopyOptions.TableLock | SqlBulkCopyOptions.FireTriggers, transaction);
// jakieś tam mapowanie kolumn
bulkCopy.DestinationTableName = "table1";
bulkCopy.BatchSize = tableSize;
bulkCopy.WriteToServer(dtTable1);
transaction.Commit();
correct = true;
}
catch (Exception ex)
{
if (transaction != null)
transaction.Rollback();
// loguj błąd i poczekaj chwilę
}
} while (!correct);
Jak widać, najpierw robię selecta, żeby otrzymać id'y tabel, później update'a na tej tabelce z id'ami, później jakieś operacje z tym związane, a następnie zrzut danych do tabelki, która ma pobrane id'y jako PK.
Na bazie jest wykonywanych sporo operacji (raz krótkich, raz bardzo długich), z różnych źródeł, więc czasami się gdzieś jakiś długi lock zdarzy, a co za tym idzie pójdzie timeout na selectcie, albo insercie/updatcie.
Teraz najważniejsze dla mnie pytanie:
Dlaczego powyższy kod po złapaniu wyjątku i ponownej próbie zapisu danych do bazy (mimo tego, że select, update i bulkcopy odbywają się w obrębie 1 transakcji) pobiera stare lub w momencie zrzucenia przez SqlBulkCopy.WriteToServer()
nieaktualne dane (jakiś inny proces uaktualnił już tabelkę z id'ami) i wyrzuca, że nie może dopisać do tej tabeli danych (ze względu na konflikt PK)?
Będę wdzięczny za każdą sensowną podpowiedź :)