Transazioni su SQL in Visual C#

mercoledì 12 luglio 2006 - 10.20

Luigi3 Profilo | Junior Member

Ciao a tutti, vi illustro velocemente il mio problema:
su una tabella di sql devo canvellare delle righe, poi leggere un database access e scrivere altre righe sulla precedente tabella in sql.
Vorrei che tutte queste operazioni vengano fatte o tutte o nessuna, per questo dovrei usare una transazione. Ho provato ma ottengo questo errore quando invio la executenonquery della delete su sql (la prima delle tre operazioni che dovrei fare):

"Thread error: Execute requires the command to have a transaction object when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized."

Apro la transazione in questo modo:
OleDbCommand SqlComm = SqlConn.CreateCommand();
oTrans = SqlConn.BeginTransaction();
SqlComm.Transaction = oTrans;

Qualìè la sequenza giusta delle aperture delle connessioni / operazioni sui database da fare?

Grazie!

alx_81 Profilo | Guru

ciao!
Non so se lo hai fatto, ma devi assegnare sia la connessione che la transazione al comando:

MyConn As New SqlConnection(tuaConnectionString) MyConn.Open() Dim MyCmd As SqlCommand = MyConn.CreateCommand() Dim MyTran As SqlTransaction MyTran = MyConn.BeginTransaction("MiaTransazione") MyCmd.Connection = MyConn MyCmd.Transaction = MyTran MyTran.Commit() 'MyTran.Rollback()
Alx81 =)

http://blogs.dotnethell.it/suxstellino

Luigi3 Profilo | Junior Member

Mi sembra di averlo fatto.
Il bello e' che se metto quel pezzo di codice all'inizio di tutto il processo, non riesco ad eseguire nessun comando. Se lo metto dopo quelle che stanno prima funzionano. E' come se bloccasse tutti gli altri comandi.

alx_81 Profilo | Guru

prova a postarmi il codice che appena ho tempo ti ci guardo.. se vuoi..
Alx81 =)

http://blogs.dotnethell.it/suxstellino

Luigi3 Profilo | Junior Member

// Aggiorno il campo Last_Refresh PRIMA della transazione
// sia sul database Oracle che sull'array delle configurazioni in memoria e su listview
SqlConn.ConnectionString = SqlConnectionString;
SqlConn.Open();

DateTime dtLastRefresh = DateTime.Now;
OleDbCommand UpdateRefresh = SqlConn.CreateCommand();
UpdateRefresh.CommandText = "UPDATE TRACE_CFG SET LAST_REFRESH = getdate() WHERE TYPE ="+ this.Config.Type +" AND MACHINE = '" + this.Config.Machine + "'";

UpdateRefresh.ExecuteNonQuery();
this.Config.LastRefresh = dtLastRefresh;

// Apro la connessione a SQL e comincio la transazione
OleDbCommand SqlComm = SqlConn.CreateCommand();
oTrans = SqlConn.BeginTransaction();
SqlComm.Transaction = oTrans;


//Cancello prima tutti i record relativi alla macchina in esame
OleDbCommand DeleteRows = SqlConn.CreateCommand();
string strDel = "";
strDel = "DELETE FROM ";
strDel += this.Config.InsertQuery.ToString();
strDel += " WHERE PCNUMBER='" + this.Config.Machine + "'";
DeleteRows.CommandText = strDel;
DeleteRows.ExecuteNonQuery();

A questo punto va in errore con il messaggio del primo post. Se metto il blocco della transazione all'inizio alla prima ExecuteNonQuery va in errore.
Grazie 1000 per la disponibilita'.

Luigi

alx_81 Profilo | Guru

fra un'oretta ti ci guardo..
Alx81 =)

http://blogs.dotnethell.it/suxstellino

alx_81 Profilo | Guru

Ok, ho trovato il tempo per risponderti.
In effetti ci sono un paio di errori:

> // Apro la connessione a SQL e comincio la transazione
> OleDbCommand SqlComm = SqlConn.CreateCommand();
> OleDbTransaction oTrans = SqlConn.BeginTransaction();
> SqlComm.Transaction = oTrans;
Qui crei una transazione e la assegni ad un comando che non usi mai. La transazione va creata da una connessione unica (esistente solo per quella transazione) ed assegnata al comando che deve essere contenuto in quella transazione
quindi:

//Cancello prima tutti i record relativi alla macchina in esame OleDbCommand DeleteRows = SqlConn.CreateCommand(); string strDel = ""; strDel = "DELETE FROM "; strDel += this.Config.InsertQuery.ToString(); strDel += " WHERE PCNUMBER='" + this.Config.Machine + "'"; //QUI!!!!! DeleteRows.Transaction=oTrans; DeleteRows.CommandText = strDel; DeleteRows.ExecuteNonQuery(); ... oTrans.Commit();

L'altro errore è che mantieni la connessione aperta (dopo l'update) sperando di poter allacciare una nuova transazione. Devi chiuderla e poi riaprirla tipo:
SqlConn.Close(); SqlConn.Open();
o al massimo creane un'altra..

Tutto qui. Un'ultima cosa, utilizza il costrutto Try Catch per gestire commit e rollback della transazione.
Ciao!
fammi sapere..


Alx81 =)

http://blogs.dotnethell.it/suxstellino

Luigi3 Profilo | Junior Member

Perfetto adesso sembra funzionare tutto bene.
Per quanto riguarda mettere tutto il codice dentro il try catch gia' l'avevo fatto, ma per semplicita' non l'avevo postato.

Mi viene un altro dubbio:

...
// Leggo tutti i record sul database locale e creo la INSERT da fare su SQL
// Alla fine di ogni ciclo, faccio una INSERT (quindi per ogni riga del database locale)
while(oReader.Read())
{

strTemp = "";
// Query di inserimento su SQL
strTemp += "INSERT INTO ";
strTemp += this.Config.InsertQuery.ToString();
strTemp += " (PCNUMBER, RECYCLETYPE, RECYCLEDESC) VALUES ('";
strTemp += this.Config.Machine + "', " + (oReader.IsDBNull(0) ? 0 : oReader.GetValue(0)) + " ,'" + (oReader.IsDBNull(1) ? "" : oReader.GetString(1)) + "')";

SqlComm.CommandText = strTemp;

try
{
if ( SqlComm.ExecuteNonQuery() == -1 )
{
strLog = "Insert error: " + strTemp;
AlertSystem.WriteLog(IsTraceActive, ref strLog, this.Config.Machine, LogType.TRACE , MessageType.Generic, tb);
}
else
{
RecordWrite++;
}

}
catch (OleDbException oEx)
{
strLog = "Insert error: " + strTemp;
AlertSystem.WriteLog(IsTraceActive, ref strLog, this.Config.Machine, LogType.TRACE, MessageType.Generic, tb);
}

In pratica in questo blocco dovrei scrivere record per record quelli che leggo dal DB access su SQL. Ma a questo punto non li faccio come transazione?

alx_81 Profilo | Guru

devi mettere la transazione (la stessa o una nidificata) anche al comando che fa l'inserimento..
poi sei sotto transazione..
Alx81 =)

http://blogs.dotnethell.it/suxstellino

Luigi3 Profilo | Junior Member

Va bene se metto cosi'?

SqlComm.Transaction = oTrans;
if ( SqlComm.ExecuteNonQuery() == -1 )
{
strLog = "Insert error: " + strTemp;
AlertSystem.WriteLog(IsTraceActive, ref strLog, this.Config.Machine, LogType.TRACE , MessageType.Generic, tb);
...
...

Oppure avendo messo SqlComm.Transaction = oTrans; all'inizio del codice prende tutti gli SqlComm?

Grazie ancora per la tua immensa disponibilità

alx_81 Profilo | Guru

Se tu imposti la transazione su un comando, fino a che non la chiudi (o in rollback o in commit), lei rimane sul comando..
quindi basta assegnarla a SqlComm..
Se però fai un'operazione di Dispose su quell'oggetto, allora la devi riassegnare..
prova e fammi sapere..
Alx81 =)

http://blogs.dotnethell.it/suxstellino

Luigi3 Profilo | Junior Member

Perfetto sembra funzionare tuttto bene. Ho segnalato una tua risposta precedente come accettata in quanto era davvero esplicativa.
Grazie ancora.
Luigi

alx_81 Profilo | Guru

di niente!
ottimo =)
ciao!
Alx81 =)

http://blogs.dotnethell.it/suxstellino
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2025
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5