I have been trying to replicate the SQLConnection.BeginTransaction(String TransactionName) for oracle. There is a class OracleConnection.BeginTransaction , but i was not able to find an overload for the method to specify the name of the transaction which needs to be used. Any help on this would be appreciated.
Thanks in Advance
I think you may need to execute the SET TRANSACTION SQL statement with the NAME parameter before starting the transaction.
You might be able to inherit from the DbConnection class and create your own overload of the BeginTransaction() method. Then you'd have to inherit from the DbTransaction class to create your own overloads of the Commit() and Rollback() methods. Then use this together with DbProviderFactory and DbCommand objects.
You can associate a Transaction with a Command object. When that command will be executed the connection will run in context of that Transaction.
There is also a Connection Property of Transaction Object which you can use to specify the Connection associated with the Transaction.
See this Example from MSDN:
using (OracleConnection connection = new OracleConnection(connectionString))
{
connection.Open();
OracleCommand command = connection.CreateCommand();
OracleTransaction transaction;
// Start a local transaction
transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
// Assign transaction object for a pending local transaction
command.Transaction = transaction;
try
{
command.CommandText =
"INSERT INTO Dept (DeptNo, Dname, Loc) values (50, 'TECHNOLOGY', 'DENVER')";
command.ExecuteNonQuery();
command.CommandText =
"INSERT INTO Dept (DeptNo, Dname, Loc) values (60, 'ENGINEERING', 'KANSAS CITY')";
command.ExecuteNonQuery();
transaction.Commit();
Console.WriteLine("Both records are written to database.");
}
catch (Exception e)
{
transaction.Rollback();
Console.WriteLine(e.ToString());
Console.WriteLine("Neither record was written to database.");
}
}
The object OracleTransaction does not have any string member that returns a "name".
I think it's the main point to face.
OracleConnection.BeginTransaction() returns an OracleTransaction object so i can't figure how it should be able to assign a name to the transaction.
I hope it helps.
Related
i have this error message:"
When the command assigned to the command is in the pending local
process, ExecuteNonQuery requires the command to have a process. The
Command property of the command has not been initialized." I get id
from first method to use for other 4 methods.
this is my code;
SqlTransaction myTransaction;
try
{
myconnection.Open();
KisiBilgiKaydet();
EgitimBilgiKaydet();
SinavBilgiKaydet();
ProgramBilgiKaydet();
BelgeKaydet();
myTransaction.Commit();
myconnection.Close();
}
catch (Exception ex)
{
myTransaction.Rollback();
myconnection.Close();
}
You should put in the transaction to each SqlCommand in all methods you needed in:
SqlTransaction transaction;
try
{
myconnection.Open();
transaction = myconnection.BeginTransaction();
KisiBilgiKaydet(transaction);
EgitimBilgiKaydet(transaction);
SinavBilgiKaydet(transaction);
ProgramBilgiKaydet(transaction);
BelgeKaydet(transaction);
transaction.Commit();
myconnection.Close();
}
catch (Exception ex)
{
transaction.Rollback();
myconnection.Close();
}
And be sure to use the transaction in the methods like:
private void sqlMethod(SqlTransaction transaction)
{
var cmd = new SqlCommand(sqlQuery, connection, sqlTransaction);
}
If you want to use SqlTransaction, then that's fine - but you need to pass the transaction in to each method (or otherwise make it available to them), and the code in each method needs to explicitly use that transaction and connection. But in principle: fine, have fun!
There is also ambient transactions aka TransactionScope - this is simpler to use, but a: doesn't work on .NET Core, and b: has a nasty habit of escalating to an MSDTC transaction (which is: bad). So personally: I'd just pass the transaction in.
I have referred this to perform rollback operation in my wpf c# application. The code that I tried is as follows:
using (OdbcConnection connection = new OdbcConnection("connectionString"))
{
OdbcCommand command = new OdbcCommand();
OdbcTransaction transaction = null;
command.Connection = connection;
try
{
connection.Open();
transaction = connection.BeginTransaction();
command.Connection = connection;
command.Transaction = transaction;
command.CommandText = "INSERT INTO TableA (A, B, C) VALUES (10,10,10)";
command.ExecuteNonQuery();
command.CommandText = "NSERT INTO TableB (D,E,F) VALUES (20,20,20)";
command.ExecuteNonQuery();
transaction.Commit();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
try
{
transaction.Rollback();
}
catch
{
}
}
Intentionally the second query has been made wrong. My intention is that when I enter the catch block on calling transaction.Rollback() the values added due to executing of the first query in TableA are not reflected since Rollback was called. However this is not the case the values are not rolledback and are present in TableA. I have searched various resources online with no luck. I cannot use SqlConnection instead of OdbcConnection my application does not support that. Is there any work around this or alternative method that can achieve what I have in mind. Please help me out.
You have basically MSDN example. I had once another problem with ODBC and the issue was with ODBC vendor drivers. I would strongly recommend check that possibility.
In my application I create a sql transaction
SqlTransaction importTrans = GPConnection.BeginTransaction();
And then I call several stored procedures in that transaction. If one of the stored procedures fails, it logs the error and it rolls back.
catch (Exception ex)
{
cError.LogException(ex);
importTrans.Rollback();
}
My question is: Is there a way for me to know, before I call the next stored procedure, that the transaction has been rolled back? Here's my code with a commented version of something I want to do.
using (SqlCommand oCmd = new SqlCommand("taHR2APP13", importTrans.Connection, importTrans))
{
oCmd.CommandType = CommandType.StoredProcedure;
oCmd.Transaction = importTrans;
//if (!importTrans.IsRolledBack)
oCmd.ExecuteNonQuery();
}
So I have this sql from c# to insert purchase info into a table (sql server 2005), and purchase items (multiple) into another table.
I would like the 2 insert statements to be run in a transaction by calling BeginTransaction.
I notice that I can do it either from the SqlConnection object or SqlCommand object. My guts tells me that I should do it through the connection object, as I will be using 1 command object for each insert, but they both share the same connection.
Am I right?
In general is there a difference between the two?
Create a SqlTransaction from the connection (BeginTransaction), then pass this in to the each SqlCommand object. There's a constructor that takes the SqlTransaction as parameter or just set the SqlCommand.Transaction property.
Something like: (sorry formatting difficult on iPad)
var tran = db.BeginTransaction();
try {
SqlCommand com = new SqlCommand(...., tran);
// or.
com.Transaction=tran;
// do the work, eg execute SQL
// finally commit the changes
tran.Commit();
}
catch
{
tran.Rollback();
}
For an application we are developing we need to read n rows from a table and then selectively update those rows based on domain specific criteria. During this operation all other users of the database need to be locked out to avoid bad reads.
I begin a transaction, read the rows, and while iterating on the recordset build up a string of update statements. After I'm done reading the recordset, I close the recordset and run the updates. At this point I commit the transaction, however none of the updates are being performed on the database.
private static SQLiteConnection OpenNewConnection()
{
try
{
SQLiteConnection conn = new SQLiteConnection();
conn.ConnectionString = ConnectionString;//System.Configuration.ConfigurationManager.AppSettings["ConnectionString"];
conn.Open();
return conn;
}
catch (SQLiteException e)
{
LogEvent("Exception raised when opening connection to [" + ConnectionString + "]. Exception Message " + e.Message);
throw e;
}
}
SQLiteConnection conn = OpenNewConnection();
SQLiteCommand command = new SQLiteCommand(conn);
SQLiteTransaction transaction = conn.BeginTransaction();
// Also fails transaction = conn.BeginTransaction();
transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
command.CommandType = CommandType.Text;
command.Transaction = transaction;
command.Connection = conn;
try
{
string sql = "select * From X Where Y;";
command.CommandText = sql;
SQLiteDataReader ranges;
ranges = command.ExecuteReader();
sql = string.Empty;
ArrayList ret = new ArrayList();
while (MemberVariable > 0 && ranges.Read())
{
// Domain stuff
sql += "Update X Set Z = 'foo' Where Y;";
}
ranges.Close();
command.CommandText = sql;
command.ExecuteNonQuery();
// UPDATES NOT BEING APPLIED
transaction.Commit();
return ret;
}
catch (Exception ex)
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
command.Dispose();
conn.Close();
}
return null;
If I remove the transaction everything works as expected. The "Domain stuff" is domain specfic and other than reading values from the recordset doesn't access the database. Did I forget a step?
When you put a breakpoint on your transaction.Commit() line do you see it getting hit?
Final answer:
SQLite's locking does not work like you're assuming see http://www.sqlite.org/lockingv3.html. Given that, I think you're having a transaction scoping issue which can be easily resolved by reorganizing your code as such:
string selectSql = "select * From X Where Y;";
using(var conn = OpenNewConnection()){
StringBuilder updateBuilder = new StringBuilder();
using(var cmd = new SQLiteCommand(selectSql, conn))
using(var ranges = cmd.ExecuteReader()) {
while(MemberVariable > 0 && ranges.Read()) {
updateBuilder.Append("Update X Set Z = 'foo' Where Y;");
}
}
using(var trans = conn.BeginTransaction())
using(var updateCmd = new SQLiteCommand(updateBuilder.ToString(), conn, trans) {
cmd.ExecuteNonQuery();
trans.Commit();
}
}
Additional notes regarding some comments in this post/answer about transactions in SQLite. These apply to SQLite 3.x using Journaling and may or may not apply to different configurations - WAL is slightly different but I am not familiar with it. See locking in SQLite for the definitive information.
All transactions in SQLite are SERIALIZABLE (see the read_uncommitted pragma for one small exception). A new read won't block/fail unless the write process has started (there is an EXCLUSIVE/PENDING lock held) and a write won't start until all outstanding reads are complete and it can obtain an EXCLUSIVE lock (this is not true for WAL but the transaction isolation is still the same).
That is the entire sequence above won't be atomic in code and the sequence may be read(A) -> read(B) -> write(A) -> read(B), where A and B represent different connections (imagine on different threads). At both read(B) the data is still consistent even though there was a write in-between.
To make the sequence of code itself atomic a lock or similar synchronization mechanism is required. Alternatively, the lock/synchronization can be created with SQLite itself by using a locking_mode pragma of "exclusive". However, even if the code above is not atomic the data will adhere to the SQL serializable contract (excluding a serious bug ;-)
Happy coding
See Locking in SQLite, SQLite pragmas and Atomic Commit in SQLite