Почему ADO BeginTrans() делает что-то отличное от BEGIN TRANSACTION?

Я столкнулся с неожиданным поведением при использовании ADO с C++ и Microsoft SQL Server 2008 (экспресс). По сути, у меня был код, который делал это:

//pseudocode pseudocode pseudocode   
adoConnection->Execute("BEGIN TRANSACTION;");
Insert( adoRecordsetPtr );
SelectAll( adoRecordsetPtr  );
adoConnection->Execute("COMMIT TRANSACTION;");

Но когда он попытался выполнить SelectAll, ADO выдало исключение со следующей информацией:

Ошибка: Ошибка ADO -2147217871: 071A14D0
Из источника: Microsoft OLE DB Provider для SQL Server
Описание: Истекло время ожидания.

Немного покопавшись, я обнаружил, что если использовать ado_connection->BeginTrans(), как это сделал бы здравомыслящий человек, все работает, как и ожидалось. И хотя этот пост в основном здесь для того, чтобы сделать обходной путь доступным для других людей, которые могут с ним столкнуться, у меня также есть вопрос:

Почему это решило проблему?

Вот немного больше подробностей о том, что происходит с моими Insert и SelectAll. Обратите внимание, что SelectAll использует объект команды ADO (поскольку в реальном коде он не выполняет выбор всего). Тайм-аут не возникает, если я использую Connection.Execute() вместо Command.Execute().

//Insert
ADODB::_RecordsetPtr prs = NULL;
HRESULT hr = prs.CreateInstance(__uuidof(ADODB::Recordset));
prs->Open(
    table
    _variant_t((IDispatch *) acpAdoConnection),
    ADODB::adOpenUnspecified, 
    ADODB::adLockOptimistic, 
    ADODB::adCmdTable);
prs->AddNew();
//put some stuff into fields using prs->Fields->Item[]
prs->Update();
prs->Close();

//SelectAll
ADODB::_CommandPtr cmd;
cmd.CreateInstance( __uuidof( ADODB::Command ) );
cmd->ActiveConnection = acpAdoConnection;
ADODB::_RecordsetPtr prs2 = NULL;
HRESULT hr2 = prs2.CreateInstance(__uuidof(ADODB::Recordset));
prs2->Open(
    table, 
    _variant_t((IDispatch *) acpAdoConnection),
    ADODB::adOpenUnspecified, 
    ADODB::adLockOptimistic, 
    ADODB::adCmdTable);
std::string sql = "SELECT * FROM [" + table + "] ;";
cmd->CommandText = sql.c_str();
_variant_t  vtEmpty (DISP_E_PARAMNOTFOUND, VT_ERROR);
_variant_t  vtEmpty2(DISP_E_PARAMNOTFOUND, VT_ERROR);
//timeout:
ADODB::_RecordsetPtr records = 
    cmd->Execute( &vtEmpty, &vtEmpty2, ADODB::adCmdText );

person Matthew Lowe    schedule 22.10.2010    source источник


Ответы (1)


Короткий ответ заключается в том, что BEGIN TRANSACTION и cn.BeginTrans() не всегда ведут себя одинаково. Эта статья MSDN расскажет вам больше об этой проблеме:


Как ведет себя ADO в отношении транзакций

По умолчанию ADO работает в режиме AutoCommit, если вы не запустите неявную транзакцию, выполнив Connection.BeginTrans.

Неявные_транзакции начинают транзакцию на сервере для каждого оператора, и фиксации не выполняются, пока они не будут выполнены вручную.

So,

set implicit_transactions on
go
insert
insert
insert

внутренне превращается в

BEGIN TRAN
insert
insert
insert
...

Вышеупомянутая транзакция не будет отменена или зафиксирована, если пользователь не выдаст правильный оператор.

Без неявной транзакции, которая по умолчанию является поведением ADO (режим автоматической фиксации), происходит следующее (концептуально):

BEGIN TRAN
insert
COMMIT TRAN
BEGIN TRAN
insert
COMMIT TRAN

Как видите, для вашего случая

BEGIN TRAN
insert
COMMIT TRAN
BEGIN TRAN
select
COMMIT TRAN

отличается от:

BEGIN TRAN
insert
select
COMMIT TRAN

... а также, возможно, не то, что вы ожидаете.

person MicSim    schedule 22.10.2010
comment
Подразумевается, что когда я явно НАЧИНАЮ без использования BeginTran(), происходят следующие события: (НАЧАЛО, НАЧАЛО, вставка, СОВЕРШЕНИЕ, НАЧАЛО, выбор, СОВЕРШЕНИЕ, СОВЕРШЕНИЕ). Но это также происходит, когда я использую Command->Execute, так что это не совсем объясняет поведение. - person Matthew Lowe; 22.10.2010
comment
Это означает, что вы получаете вложенную транзакцию, поэтому первая запущенная еще не зафиксирована при выполнении ваших двух транзакций. Я предполагаю, что данные все еще заблокированы, поскольку транзакция еще не завершена. - person MicSim; 22.10.2010
comment
Я хочу понять, что данные все еще заблокированы. Описанная программа работает, если SelectAll реализован как Connection->Execute(select ), но ломается, если я использую Command->Execute(,,select). - person Matthew Lowe; 23.10.2010