USANDO TRANSACOES PARA ATUALIZAR MULTIPLAS TABELAS

LUIS.HERRERA 22/04/2013 09:11:01
#422252
C# 2008 + SQL Express 2005

Estou tentando atualizar inicialmente 2 tabela numa transação (inclusão e alteração), de várias maneiras, mas sempre recebe erro na transação dizendo que:

Exception = -532459699 [Ô] SqlConnection não dá suporte para transações paralelas[Ô].

Primeiro tentei assim:

SqlTransaction transacao = null;
transacao = Dados.cn.BeginTransaction(IsolationLevel.Serializable);
cmd.Transaction = transacao; // fará inclusão na tabela 1
cmdEmail.Transaction = transacao; // fará inclusões na tabela 2
//Nota cada command tem seus parâmetros e valores, pois o cmd é o registro único da primeira tabela, já o cmdEmails tem vários itens da segunda tabela.


Segundo tentei assim:

SqlTransaction transacao = null;

try
{
string sql = [Ô]UPDATE DEPARTAMENTOS SET DEPTO = @DEPTO,INATIVO = @INATIVO WHERE ((ID = @ID) AND (ID_EMPRESA = @ID_EMPRESA));[Ô];
SqlCommand cmd = new SqlCommand(sql, Dados.cn);
cmd.Parameters.Add([Ô]@ID[Ô], SqlDbType.Int);
cmd.Parameters[[Ô]@ID[Ô]].Value = departamento.ID;
cmd.Parameters.Add([Ô]@ID_EMPRESA[Ô], SqlDbType.Int);
cmd.Parameters[[Ô]@ID_EMPRESA[Ô]].Value = departamento.ID_EMPRESA;
cmd.Parameters.Add([Ô]@DEPTO[Ô], SqlDbType.NVarChar);
cmd.Parameters[[Ô]@DEPTO[Ô]].Value = departamento.DEPTO;
cmd.Parameters.Add([Ô]@INATIVO[Ô], SqlDbType.Bit);
cmd.Parameters[[Ô]@INATIVO[Ô]].Value = departamento.INATIVO;

// prepara estrutura dos emails desse departamento
Int32 novoEmailGravado = 0; // usado para validar a gravação
EmailsDAL myEmailGravando = new EmailsDAL(); // usar DAL dos emails para testar se já existe antes de gravar

string sqlEmail = [Ô]INSERT INTO EMAILS (IDOrigem, TipoDoCadastro, Email) VALUES (@IDOrigem,@TipoDoCadastro,@Email);[Ô];

Dados.AbrirConexao();
transacao = Dados.cn.BeginTransaction(IsolationLevel.Serializable); //default
cmd.Transaction = transacao;

//grava alterações do departamento
cmd.ExecuteNonQuery();

//para resolver problema de transação com acesso ao EmailDAT ver se existe um email já cadastrao, eu excluo todos antes, assim não tem o problema de abrir a conexão e testar outro processo durante esta atualização.

cmd.Parameters.Clear();
cmd.CommandText = [Ô]DELETE * FROM EMAILS WHERE ((IDOrigem = @IDOrigem) AND (TipoDoCadastro = @TipoDoCadastro));[Ô];
cmd.Parameters.Add([Ô]@IDOrigem[Ô], SqlDbType.Int);
cmd.Parameters.Add([Ô]@TipoDoCadastro[Ô], SqlDbType.TinyInt);
cmd.Parameters[[Ô]@IDOrigem[Ô]].Value = departamento.ID;
cmd.Parameters[[Ô]@TipoDoCadastro[Ô]].Value = 0; // 0 = Departamentos


// cria nova estrutura do command para gravação de todos os emails para este departamento
cmd.Parameters.Clear();
cmd.CommandText = sqlEmail;
cmd.Parameters.Add([Ô]@IDOrigem[Ô], SqlDbType.Int);
cmd.Parameters.Add([Ô]@TipoDoCadastro[Ô], SqlDbType.TinyInt);
cmd.Parameters.Add([Ô]@Email[Ô], SqlDbType.NVarChar);

// inicio gravação dos emails
foreach (EmailModelo myEmailAchado in myListEmailsRecebidos)
{
cmd.Parameters[[Ô]@IDOrigem[Ô]].Value = departamento.ID;
cmd.Parameters[[Ô]@TipoDoCadastro[Ô]].Value = myEmailAchado.TipoDoCadastro;
cmd.Parameters[[Ô]@Email[Ô]].Value = myEmailAchado.Email;
cmd.ExecuteNonQuery(); //-> não retorna dados, pois não preciso agora saber o ID do email, só gravar.
}

//-> gravou tudo sem problemas, então finaliza
transacao = Dados.cn.BeginTransaction(IsolationLevel.ReadCommitted);
transacao.Commit();
}
catch (SqlException)
{
transacao.Rollback();
throw;
}
catch (Exception)
{
transacao.Rollback();
throw;
}
finally
{
Dados.FecharConexao();
}


De qualquer maneira dá erro da transação paraleta.

Como fazer modificações em mais de uma tabala com transação funcionando, o que pode estar errado?

OCELOT 22/04/2013 11:04:13
#422262
No geral as operações com transações devem ocorrer na seguinte ordem

Abre a Conexão
Inicia a Transação
Executa INSERTs, UPDATEs ou DELETEs passando a transação para o Command, repetindo quantas vezes necessário
Executa o Commit na transação se tudo está OK ou executa o Rollback caso algo tenha dado errado
Fecha a Conexão

Agora tem um porém, se você tiver o [Ô]MultipleActiveResultSets=true[Ô] na connection string isso pode dar problema se você já tiver alguma coisa aberta nessa conexão, pelo que me parece se você tiver por exemplo um DataReader aberto nessa conexão você não pode executar um Commit ou Rollback, eu diria que geralmente é mais fácil usar mais de uma conexão do que usar o [Ô]MultipleActiveResultSets=true[Ô], eu geralmente abria uma nova conexão para cada DataReader que eu precisasse, inclusive existe um parâmetro que se passa para o ExecuteReader que faz com que quando o DataReader seja fechado ele feche a conexão dele também, o que torna isso mais prático
LUIS.HERRERA 22/04/2013 15:06:20
#422291
Oi OCELOT.

Não sei o que é esse MultipleActiveResultSets que citou, pois não usei ou vi nada sobre isso até agora. Mas pela sua explicação, na abertura do form uso um código com um DataTable para preencher um DataGridView. Estou trocando todos os DataTable por List, assim não fico com nenhum objeto preso a conexão.

Porém fiz outras mudanças no código, não sei exatamente se foi uma combinação deles, mas agora está funcionando.

1- Descobrindo que no SQL Server não se pode usar (*) como fazia no MS Access:

Delete * From ....

troquei para
DELETE FROM....

e funcionou.

2- Tive de tirar IsolationLevel.Serializable, pois não funcionava de forma alguma, então deixei sem parâmetros e resolveu:

 transacao = Dados.cn.BeginTransaction();


Tópico encerrado , respostas não são mais permitidas