Transaction Aborted in C# transaction scope - c#

I saw so many example while googling regarding Transaction Scope.But i tried to do that,So in my case it throws the exception.Exception shows "Transaction Aborted".. I saw ther's may solution it shows Close the connection.but here i close it in finally block.Can anyone helpuot to solve this issue.
UPDATED
Above connection issue solved.Now ti throws the exception in db.ExecuteNonQuery(cmd); section.Accroding to my knowledge until thrasaction has completed this doen't executed.
MyCode
public bool NewsType(int Id)
{
bool status = false;
DatabaseProviderFactory factory = new DatabaseProviderFactory();
Database db = factory.Create("NewsCon");
con.Open();
using (TransactionScope transactionScope = new TransactionScope())
{
try
{
String query = #"DELETE FROM NewsType WHERE ID =: ID";
cmd = db.GetSqlStringCommand(query);
db.AddInParameter(cmd, "ID", DbType.Int32, Id);
db.ExecuteNonQuery(cmd); //<-- Now it shows from the error here.But i think inside transaction scope this doesn't execute until transaction has completed.
//Second step goes here
status = true;
transactionScope.Complete();
transactionScope.Dispose();
}
catch (Exception ex)
{
throw;
}
finally
{
con.Close();
}
return status;
}
}

Related

Oracle connection issue while using tasks and blocking collection

I have some tasks (nWorkers = 3):
var taskFactory = new TaskFactory(cancellationTokenSource.Token,
TaskCreationOptions.LongRunning, TaskContinuationOptions.LongRunning,
TaskScheduler.Default);
for (int i = 0; i < nWorkers; i++)
{
var task = taskFactory.StartNew(() => this.WorkerMethod(parserItems,
cancellationTokenSource));
tasks[i] = task;
}
And the following method called by the tasks:
protected override void WorkerMethod(BlockingCollection<ParserItem> parserItems,
CancellationTokenSource cancellationTokenSource)
{
//...log-1...
using (var connection = new OracleConnection(connectionString))
{
OracleTransaction transaction = null;
try
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
connection.Open();
//...log-2...
transaction = connection.BeginTransaction();
//...log-3...
using (var cmd = connection.CreateCommand())
{
foreach (var parserItem in parserItems.GetConsumingEnumerable(
cancellationTokenSource.Token))
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
try
{
foreach (var statement in this.ProcessRecord(parserItem))
{
cmd.CommandText = statement;
try
{
cmd.ExecuteNonQuery();
}
catch (OracleException ex)
{
//...log-4...
if (!this.acceptedErrorCodes.Contains(ex.Number))
{
throw;
}
}
}
}
catch (FormatException ex)
{
log.Warn(ex.Message);
}
}
if (!cancellationTokenSource.Token.IsCancellationRequested)
{
transaction.Commit();
}
else
{
throw new Exception("DBComponent has been canceled");
}
}
}
catch (Exception ex)
{
//...log-5...
cancellationTokenSource.Cancel();
if (transaction != null)
{
try
{
transaction.Rollback();
//...log-6...
}
catch (Exception rollbackException)
{
//...log-7...
}
}
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
connection.Close();
//...log-8...
}
}
//...log-9...
}
There is a producer of ParserItem objects and these are the consumers. Normally it works fine, there are sometimes that there is an Oracle connection timeout, but in these cases I can see the exception message and everything works as designed.
But sometimes the process get stuck. When it gets stuck, in the log file I can see log-1 message and after that (more or less 15 seconds later) I see log-8 message, but what is driving me nuts is why i cannot see neither the exception message log-5 nor the log-9 message.
Since the cancellationTokenSource.Cancel() method is never called, the producer of items for the bounded collection is stuck until a timeout two hours later.
It is compiled for NET Framework 4 and I'm using Oracle.ManagedDataAccess libraries for the Oracle connection.
Any help would be greatly appreciated.
You should never dispose a transaction or connection when you use using scope. Second, you should rarely rely on exception based programming style. Your code rewritten below:
using (var connection = new OracleConnection(connectionString))
{
using (var transaction = connection.BeginTransaction())
{
connection.Open();
//...log-2...
using (var cmd = connection.CreateCommand())
{
foreach (var parserItem in parserItems.GetConsumingEnumerable(cancellationTokenSource.Token))
{
if (!cancellationTokenSource.IsCancellationRequested)
{
try
{
foreach (var statement in ProcessRecord(parserItem))
{
cmd.CommandText = statement;
try
{
cmd.ExecuteNonQuery();
}
catch (OracleException ex)
{
//...log-4...
if (!acceptedErrorCodes.Contains(ex.ErrorCode))
{
log.Warn(ex.Message);
}
}
}
}
catch (FormatException ex)
{
log.Warn(ex.Message);
}
}
}
if (!cancellationTokenSource.IsCancellationRequested)
{
transaction.Commit();
}
else
{
transaction.Rollback();
throw new Exception("DBComponent has been canceled");
}
}
}
}
//...log-9...
Let me know if this helps.
I can confirm everything you're saying. (program stuck, low CPU usage, oracle connection timeouts, etc.)
One workaround is to use Threads instead of Tasks.
UPDATE: after careful investigation I found out that when you use a high number of Tasks, the ThreadPool worker threads queued by the Oracle driver become slow to start, which ends up causing a (fake) connect timeout.
A couple of solutions for this:
Solution 1: Increase the ThreadPool's minimum number of threads, e.g.:
ThreadPool.SetMinThreads(50, 50); // YMMV
OR
Solution 2: Configure your connection to use pooling and set its minimum size appropriately.
var ocsb = new OracleConnectionStringBuilder();
ocsb.DataSource = ocsb.DataSource;
ocsb.UserID = "myuser";
ocsb.Password = "secret";
ocsb.Pooling = true;
ocsb.MinPoolSize = 20; // YMMV
IMPORTANT: before calling any routine that creates a high number of tasks, open a single connection using that will "warm-up" the pool:
using(var oc = new OracleConnection(ocsb.ToString()))
{
oc.Open();
oc.Close();
}
Note: Oracle indexes the connection pools by the connect string (with the password removed), so if you want to open additional connections you must use always the same exact connect string.

SqlException: do not abort a transaction

I have a code that adds data to two EntityFramework 6 DataContexts, like this:
using(var scope = new TransactionScope())
{
using(var requestsCtx = new RequestsContext())
{
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
logsCtx.SaveChanges();
}
}
}
There is an insert trigger in Requests table that rejects some values using RAISEERROR. This situation is normal and should be handled by the try-catch block where the SaveChanges method is invoked. If the second SaveChanges method fails, however, the changes to both DataContexts must be reverted entirely - hence the transaction scope.
Here goes the error: when requestsCtx.SaveChanges() throws a exception, the whole Transaction.Current has its state set to Aborted and the latter logsCtx.SaveChanges() fails with the following:
TransactionException:
The operation is not valid for the state of the transaction.
Why is this happening and how do tell EF that the first exception is not critical?
Really not sure if this will work, but it might be worth trying.
private void SaveChanges()
{
using(var scope = new TransactionScope())
{
var log = CreateRequest();
bool saveLogSuccess = CreateLogEntry(log);
if (saveLogSuccess)
{
scope.Complete();
}
}
}
private LogEntry CreateRequest()
{
var req = new Request { Id = 1, Value = 2 };
var log = new LogEntry { RequestId = 1, State = "OK" };
using(var requestsCtx = new RequestsContext())
{
requestsCtx.Requests.Add(req);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
finally
{
return log;
}
}
}
private bool CreateLogEntry(LogEntry log)
{
using(var logsCtx = new LogsContext())
{
try
{
logsCtx.Logs.Add(log);
logsCtx.SaveChanges();
}
catch (Exception)
{
return false;
}
return true;
}
}
from the documentation on transactionscope: http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx
If no exception occurs within the transaction scope (that is, between
the initialization of the TransactionScope object and the calling of
its Dispose method), then the transaction in which the scope
participates is allowed to proceed. If an exception does occur within
the transaction scope, the transaction in which it participates will
be rolled back.
Basically as soon as an exception is encountered, the transaction is rolled back (as it seems you're aware) - I think this might work but am really not sure and can't test to confirm. It seems like this goes against the intended use of transaction scope, and I'm not familiar enough with exception handling/bubbling, but maybe it will help! :)
I think I finally figured it out. The trick was to use an isolated transaction for the first SaveChanges:
using(var requestsCtx = new RequestsContext())
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
using(var outerScope = new TransactionScope())
{
using(var innerScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{
requestsCtx.SaveChanges();
innerScope.Complete();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
}
logsCtx.SaveChanges();
outerScope.Complete();
}
}
Warning: most of the articles about RequiresNew mode discourage using it due to performance reasons. It works perfectly for my scenario, however if there are any side effects that I'm unaware of, please let me know.

Error when Inserting using SqlMapperExtension Dapper

Currently playing around with Dapper I'm trying to insert values into the db as follows
using (var sqlCon = new SqlConnection(Context.ReturnDatabaseConnection()))
{
sqlCon.Open();
try
{
var emailExists = sqlCon.Query<UserProfile>(#"SELECT UserId FROM User_Profile WHERE EmailAddress = #EmailAddress",
new { EmailAddress = userRegister.EmailAddress.Trim() }).FirstOrDefault();
if (emailExists == null) // No profile exists with the email passed in, so insert the new user.
{
userProfile.UniqueId = Guid.NewGuid();
userProfile.Firstname = userRegister.Firstname;
userProfile.Surname = userRegister.Surname;
userProfile.EmailAddress = userRegister.EmailAddress;
userProfile.Username = CreateUsername(userRegister.Firstname);
userProfile.Password = EncryptPassword(userRegister.Password);
userProfile.AcceptedTerms = true;
userProfile.AcceptedTermsDate = System.DateTime.Now;
userProfile.AccountActive = true;
userProfile.CurrentlyOnline = true;
userProfile.ClosedAccountDate = null;
userProfile.JoinedDate = System.DateTime.Now;
userProfile.UserId = SqlMapperExtensions.Insert(sqlCon, userProfile); // Error on this line
Registration.SendWelcomeEmail(userRegister.EmailAddress, userRegister.Firstname); // Send welcome email to new user.
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
sqlCon.Close();
}
}
The error I get is
ExecuteNonQuery requires the command to have a transaction when the connection
assigned to the command is in a pending local transaction. The Transaction
property of the command has not been initialized.
I have googled this error, but I misunderstood the answers provided.
From the error message I assume that you have started a transaction that was neither committed nor rolled back. The real cause for this error message is elsewhere.
I suggest you to log requests in Context.ReturnDatabaseConnection() and trace what requests precede this error.
Also I advice you to look in your code for all transactions and check if they are correctly completed (commit/rollback).

Transactionscope rollback

I have a method inside a main one. I need the child method to be able to roll back if the parent method fails. The two data connections use different servers . Before I added the transaction scopes, they worked well. But when I tie them together, the child method aborts.
Edit: Error message: Network access for distributed transaction Manager(MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using Component Service Administrative tool.
public static void LoopStudent()
{
try
{
using(TransactionScope scope = new TransactionScope())
{
String connString = ConfigurationManager.AppSettings["DBConnection"];
using(SqlConnection webConn = new SqlConnection(connString))
{
webConn.Open();
String sql = "select * from students";
using(SqlCommand webComm = new SqlCommand(sql, webConn))
{
using(SqlDataReader webReader = webComm.ExecuteReader())
{
if (webReader.HasRows)
{
while (webReader.Read())
{
int i = GetNextId();
}
}
else
Console.WriteLine("wrong");
}
}
}
scope.Complete();
}
}
catch (Exception ex)
{
Console.WriteLine("Error " + ex.Message);
}
} //End LoopThroughCart
public static int GetNextId(String str)
{
int nextId = 0;
String connString = ConfigurationManager.AppSettings["SecondDBConnection"];
try
{
using(TransactionScope scope = new TransactionScope())
{
using(SqlConnection webConn = new SqlConnection(connString))
{
webConn.Open();
using(SqlCommand webComm = new SqlCommand("GetNextId", webConn))
{
//do things
}
}
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
Console.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
Console.WriteLine("ApplicationException Message: {0}", ex.Message);
}
return nextId;
} //End GetNextId
If you do not use RequireNew in you inner method, the inner method will be automatically rolled back if the parent fails to commit the transaction.
What error are you getting?

Int.TryParse not resolving specified cast is not valid error

Alright I originally started out using a Convert.ToInt32(myradTextBox.Text) then it said specified cast is not valid. I did some research on here and decided to try Int.TryParse. Upon doing so I still received this error. What I am trying to do is when the user enters an ID and hits the create button, it searches the DB to see if that ID is already there. I have also tried to convert the bool value from my Int.TryParse to int using Convert.ToInt32(Result) still same error (see below in third code post for where that would be posted). Maybe it has something to do with my comparison method.
Below I have provided the Int.TryParse method with values. The Method I am calling to check the userinput is not in the db currently and my if statement that is catching the statement. Any input on how to fix this would be greatly appreciated. I am still new to most of this stuff so I apologize if leaving any critical info off. Just ask if you need clarification or something elaborated.
Here is my method for comparison:
public bool isValidID(int id)
{
SqlConnection dbConn = null;
int count = 0;
try
{
using (dbConn = new SqlConnection(Properties.Settings.Default["tville"].ToString()))
{
string sql = "SELECT Count(*) FROM PackLabelFormat where PackFormatID = #PackFormatID";
SqlCommand cmd = dbConn.CreateCommand();
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("#PackFormatID", id);
dbConn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
reader.Read();
count = reader.GetInt16(0);
}
}
}
catch (Exception ex)
{
throw ex;
}
if (count > 0)
return false;
return true;
}
Here is my variables that I use in my Int.TryParse method:
string IDselect = rTxtBoxFormatID.Text.ToString();
int resultInt;
bool result = int.TryParse(IDselect, out resultInt);
Lastly here is my method that is catching the error:
SqlConnection dbConn = null;
LabelData labelList = new LabelData();
try
{
using (dbConn = new SqlConnection(Properties.Settings.Default["tville"].ToString()))
{
if (SelectedVersion.isValidID(resultInt))
{
SelectedVersion.PackFormatID = resultInt;
}
else
{
MessageBox.Show("ID already in use!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
catch (Exception ex)
{
throw ex;
}
The database column did not support Int16 aka short. Which was why my specified cast is not valid error never went away no matter what I tried. Thank you for your help in this matter! Here is the code to further illustrate what the problem was.
using (SqlDataReader reader = cmd.ExecuteReader())
{
reader.Read();
//count = reader.GetInt16(0); needs to be reader.GetInt32(0);
}

Categories

Resources