I am facing some strange error while executing following code.
I debugged all methods and found no error but scope.Complete(); line returns an error "Transaction has aborted" and InnerException is "The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION."
InsertEntries(myObject) calls few more methods within for making entries to near about 12-15 different tables in SQL Server. It used StoredProcedues to do that.
I am using EntityFramework and SQLServer.
NOTE:
1 - Everything works fine without TransactionScope.
2 - Error returns on scope.Complete(); line. No other errors.
using (TransactionScope scope = new System.Transactions.TransactionScope()
{
try
{
myObject.IsMain = false;
InsertEntries(myObject);
myObject.IsDuplicateNumber = IsDuplicateNumber;
myObject.RecNum = RecNum;
myObject.NextNumber = nextNumber;
scope.Complete();
}
catch (Exception ex)
{
throw ex;
}
}
Related
I am using the transaction scope from System.Transactions.
I have this method where I have two insertions in database. The first Localization is inserted, but then rolled back since it fails on the second insertion.
Now the error is not with the data I send. The data is good. When I remove the transaction scope it works.
I get this error:
System.InvalidOperationException: A root ambient transaction was completed before the nested transaction. The nested transactions should be completed first.
It also enters the second catch and disposes the scope. What could be the problem?
This is my code:
public async Task InsertCategory(InsertCategoryRequest request)
{
using var scope = new TransactionScope();
int localizationId;
try
{
localizationId = await _localizationRepository.InsertLocalization(new Localization
{
English = request.NameEN,
Albanian = request.NameAL,
Macedonian = request.NameMK
});
}
catch (Exception e)
{
scope.Dispose();
Log.Error("Unable to insert localization {#Exception}", e);
throw ExceptionHandler.ThrowException(ErrorCode.Localization_UnableToInsert);
}
try
{
await _categoryRepository.InsertCategory(new Category
{
Name = request.NameEN,
LocalizationId = localizationId
});
}
catch (Exception e)
{
scope.Dispose();
Log.Error("Unable to insert category {#Exception}", e);
throw ExceptionHandler.ThrowException(ErrorCode.Category_UnableToInsert);
}
scope.Complete();
scope.Dispose();
}
I found the answer. I looked for such a long time, but after I posted I found the answer lol.
Just added TransactionScopeAsyncFlowOption.Enabled when constructing the Transaction Scope.
My stored procedure is throwing custom errors to handle validation within a multi user web app. This is working as expected in SQL Server with error number 50001 being returned however when my C# code catches the error it always has the error number 50000 instead of 50001 therefore I cannot treat the errors differently.
How can I catch error number 50001? Note that I am using Dapper to execute all stored procedures.
SQL
THROW 50001, 'Client already has an Active Visit! THIS IS DEV!!!!',1;
Msg 50001, Level 16, State 1, Line 1
Client already has an Active Visit! THIS IS DEV!!!!
C#
catch (SqlException ex)
{
var errorHandler = new ErrorHandler();
var msg = errorHandler.ErrorMessage(ex);
if (ex.Number == 50001)
{
return BadRequest(msg);
}
else
{
return StatusCode(500, msg);
}
}
catch (Exception ex)
{
var errorHandler = new ErrorHandler();
return StatusCode(500, errorHandler.ErrorMessage(ex));
}
EXAMPLE
SQL server error 50000 is reserved for general user defined message, so it sounds like it's not finding error 50001 in sys.messages. You can try to add the error using the stored procedure sp_addmessage.
Once that is done, you can call it like this:
RAISERROR(50001, 1, 1)
Another reason it could be error 50000 is if you are raising the SQL error in a SQL TRY/CATCH as that will always return a 50000 error code.
Just tried this code myself:
using (var cmd = new SqlCommand("THROW 50001,'error',1;", conn))
{
cmd.ExecuteNonQuery();
}
And that does return ex.Number 50001 so it must be a problem in your SQL. Like I said above, it could be caused by a TRY/CATCH as this won't save the original error code number.
Building an C# MVC webapplication with Linq to Sql for querying the SQL database.
I am trying to catch an exception if the database should go offline to avoid the yellow screen of death.
After debugging, I found two things. First, the creation of the DataContext does not check if the sql server is available. Second, there is an Error message (Or an exception!?) stored in the rows variable.
The Catch statement in the code below is never reached. So I get the Yellow screen of death when trying to read data from the Model in the View.
Why isn't the Catch statement reached?
Code:
try
{
//Creating DB Context
var con = ConfigurationManager.ConnectionStrings["teststring"].ConnectionString;
TestDataContext db = new TestDataContext(con);
//Querying database. This should cause an exception to be thrown!?
var rows = from s in db.Table
orderby s.Id descending
select s;
//Returning the View with the data
return View(rows);
}
catch (Exception ex)
{
ErrorInfo err = new ErrorInfo("Something went wrong when trying to query the database. See the log for details.");
err.WriteToErrorLog(ex);
return View("Error", err);
}
have you tried to use SQLException
try
{
//Creating DB Context
var con = ConfigurationManager.ConnectionStrings["teststring"].ConnectionString;
TestDataContext db = new TestDataContext(con);
//Querying database. This should cause an exception to be thrown!?
var rows = from s in db.Table
orderby s.Id descending
select s;
//Returning the View with the data
return View(rows);
}
catch (SqlException ex)
{
ErrorInfo err = new ErrorInfo("Something went wrong when trying to query the database. See the log for details.");
err.WriteToErrorLog(ex);
return View("Error", err);
}
Firstly: as #ofir mentioned you should call .ToList() method in order to execute LINQ statement. so you should call it on your try block to be able to catch the exception.
Secondly:
you shouldn't show Yellow error screen to end users because of security reasons, you can enable CustomError in web.config file to show a default page when an exception is thrown and redirect it to an Error page
<customErrors mode="On" defaultRedirect="~/ErrorPages/GeneralError">
</customErrors>
I have a code like this:
try
{
Member member = database.Members.Where(m=>m.ID=1).FirstOrDefault();
member.Name = "NewMemberName";
database.Entry(member).State = EntityState.Modified;
database.SaveChanges();
}
catch (Exception ex)
{
database.Logs.Add(new Log() { Value=ex.ToString() });
database.SaveChanges();
}
And Entity:
[StringLength(5)]
public string Name { get; set; }
If the Name String more than 5 it would be error and catch the exception ,but when I add a log then save ,the exception from SaveChange(); still remains,how should I do?(Can't change the schema)
the exception from SaveChange(); still remains
Well, if this throws an exception:
database.SaveChanges();
Then there's a pretty good chance that this will also throw an exception:
database.SaveChanges();
Basically, in your catch block you shouldn't be immediately re-trying the operation that just failed a millisecond ago. Instead, log the failure and handle the exception:
catch (Exception ex)
{
// DO NOT call SaveChanges() here.
}
Of course, if writing to the database is failing, then logging to the database is also likely to fail. Suppose for example that the connection string is wrong or the database is down or timing out. You can't log that.
I recommend using a logging framework (log4net, NLog, etc.) as a separate dependency from your Entity Framework data access layer. It's a small learning curve, but you end up with a pretty robust logging system that can much more effectively handle problems. And can be easily configured to log to multiple places, so if writing to one error log (the database) fails then you still have another one (a file, for example).
At the very least, if persisting your data context fails, you'll need to log to a new data context. Otherwise the part that failed is still there.
Something structurally more like this:
try
{
using (var database = new DbContext())
{
Member member = database.Members.Where(m=>m.ID=1).FirstOrDefault();
member.Name = "NewMemberName";
database.Entry(member).State = EntityState.Modified;
database.SaveChanges();
}
}
catch (Exception ex)
{
using (var database = new DbContext())
{
database.Logs.Add(new Log() { Value=ex.ToString() });
database.SaveChanges();
}
}
My Code something like this
try
{
using (TransactionScope iScope = new TransactionScope())
{
try
{
isInsertSuccess = InsertProfile(account);
}
catch (Exception ex)
{
throw;
}
if (isInsertSuccess)
{
iScope.Complete();
retValue = true;
}
}
}
catch (TransactionAbortedException tax)
{
throw;
}
catch (Exception ex)
{
throw;
}
Now what happen is that even if my value is TRUE a TransactionAbortedException Exception occurs randomly, but data get's inserted/updated in DB.
Any idea what went wrong?
As the TransactionAbortedException documentation says,
This exception is also thrown when an attempt is made
to commit the transaction and the
transaction aborts.
This is why you see the exception even after calling Transaction.Complete: the Complete method is not the same thing as Commit:
calling this method [TransactionScope.Complete] does not guarantee
a commit of the transaction. It is
merely a way of informing the
transaction manager of your status
The transaction isn't committed until you exit the using statement: see the CommittableTransaction.Commit documentation for details. At that point any actions participating in the transaction may vote to abort the transaction and you'll get a TransactionAbortedException.
To debug the underlying problem you need to analyze the exception details and stack trace. As Mark noted in a comment, it may well be caused by a deadlock or another interaction with other database processes.