BeginTransaction on localdb - c#

I have an connection to LocalDB database:
Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-mysite-20160510105433.mdf
I used code first with Entity Framework and the following piece of code:
var tran = this.context.Database.BeginTransaction();
//some operations on dbcontext
tran.Rollback();
where context variable is simple DbContext instance.
THe exception "The underlying provider failed on rollback" is thrown.
Such an exception is not thrown when using normal, SQL Server connection.
Does it mean that localdb doesn't support transaction? If yes, how to achieve it?
Or does it come from that connections run per process, so when query is over then connection is closed automatically?
Edit: sample operations that I perform:
var myEntity = new MyEntityA();
this.context.MyEntitiesA.Add(myEnttiy).
this.context.Save(); //save to retreive id
// some playing with id
var mySecondEntity= new MyEntityB(){ MyEntityAId = myEntityA.Id, //other data gethered in "playing part" } ;
this.context.MyEntitiiesB.Add(mySecondEntity).
this.context.Save(); //need to rollback first, when here fails.
So I want to enclose within transaction:
using (var tran = this.context.Database.BeginTransaction())
{
try
{
// operations
tran.Commit();
}
catch (Exception ex)
{
// stuff
tran.Rollback();
}
}
So as you can see transaction is useful here.

Related

EF6. Save exception information to database after rollback transaction

I need to save the information about an exception to the database.
i.e
SomeMethod()
{
try
{
using(transaction = context.beginTransaction())
{
try
{
// here is the database error
await transaction.CommitAsync();
}
catch(Exception e)
{
await transaction.Rollback();
throw;
}
}
}
catch(Exception exception)
{
// Here I get the same error that was generated inside the transaction
context.Set<LogEntity>().Add(new LogEntity(....));
await context.SaveChangesAsync();
}
}
Do I understand correctly that a context is a connection to a database and that in one context I can perform several transactions?
As I understand it, the context in this stage is in a dirty state?
How can I write to the database after a transaction error has occurred?
As I understand it, the context in this stage is in a dirty state?
Correct. The "database error" must have occurred in a SaveChanges(Async) call, so if you repeat that call on the same context, the exception will occur again. The remedy is simple: use a new context in the catch block.
Side note: catching the exception in the using block isn't necessary. If the transaction isn't committed before it's disposed, it will be rolled back.

The transaction has aborted error though

I am using EF6 with code first approach. I have a case where I am trying to apply same transaction over 2 different databases. Find my code below for the reference,
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, TimeSpan.FromSeconds(6000)))
{
using (var dbContext = new MyDbContext())
{
try
{
/*
* some operations on dbContext
*
*/
using (IDbConnection conn = new SqlConnection(NewConnectionString))
{
string query = " ALTER TABLE [dbo].[TableName]...........";
conn.Execute(query); // I am using a dapper to call this execute query
}
}
catch (Exception ex)
{
throw;
}
}
scope.Complete();
}
The first section in try block does some normal operations on tables by using linq.
The second section where I have used IDbConnection, I am executing some query using dapper on another database. I can execute everything fine without error, but I'm getting error after I complete the scope. It throws error as The transaction has aborted. Everything works fine whenever I skip dapper execution section. I have checked many solutions and also tried to put separate transaction for dapper section but nothing worked. My requirement is to put all the statements under single transaction. Is that possible? How?

Nested transaction scope .net

I'm using SQL Server 2008 R2 and trying to use transactions.
First a question about transactions in .net and SQL Server. If I have something like this
try {
var transactionOption = new TransactionOptions();
transactionOption.IsolationLevel = IsolationLevel.ReadCommitted;
transactionOption.Timeout = TransactionManager.MaximumTimeout;
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, transactionOption)) {
//create question this creates a new question in the database
Helpers.CreateQuestionBankItem(ref mappedOldNewQuestionItemGuid, missingQuestionBankItems);
//question created
//query database for the code of the newly inserted question, will the database give me the code since Complete has not been called as yet?
scope.Complete();
}
}
catch (Exception ex) {
throw;
}
//query database for the code of the newly inserted question, will the database give me the code since Complete has been called as now?
At which point should I call the database to ask for the code of the newly inserted question. Now my second question, before I ask I found this link Nested Transaction . In the light of the above link I want to still ask that if I have something like this
try {
var transactionOption = new TransactionOptions();
transactionOption.IsolationLevel = IsolationLevel.ReadCommitted;
transactionOption.Timeout = TransactionManager.MaximumTimeout;
using (var outerscope = new TransactionScope(TransactionScopeOption.RequiresNew, transactionOption)) {
try {
var transactionOption = new TransactionOptions();
transactionOption.IsolationLevel = IsolationLevel.ReadCommitted;
transactionOption.Timeout = TransactionManager.MaximumTimeout;
using (var innerscope = new TransactionScope(TransactionScopeOption.RequiresNew, transactionOption)) {
//create question this creates a new question in the database
Helpers.CreateQuestionBankItem(ref mappedOldNewQuestionItemGuid, missingQuestionBankItems);
//question created
//query database for the code of the newly inserted question, will the database give me the code since Complete has not been called as yet?
innerscope.Complete();
}
}
catch (Exception ex) {
}
//query database for the code of the newly inserted question, will the database give me the code since Complete has been called as now?
outerscope.Complete();
}
}
catch (Exception ex) {
throw;
}
If my innerscope completes, will querying SQL Server give me the code of the newly created question.
What happens if the inner scope throws an exception and I gobble it up, will the outer scope also be disposed off?
Does calling innerscope.Complete() completes that inner scope?
If you want to recover from a failure in a transactional context you need to use transaction savepoints. Unfortunately the managed System.Transaction has no support for savepoints. Not only that, but you won't be able to use savepoints, even directly, if you use transaction scopes, because the transaction scope will escalate to distributed transactions and savepoints do not work in distributed contexts.
You can use instead the platform specific SqlTransaction which supports Save() for savepoints. See Exception Handling and Nested Transactions for an example of transaction-aware exception handling.

nHibernate Doesn't update other table through Oracle Package in same session

In a window application we are using nHibernate. We are facing problem when updating data a table (Tag1 or Tag2) and in the same ISession we are inserting data from the table into another table (QA Table) using Oracle Package. On commit Oracle package doesnt't see the changed data in the Tag1/Tag2 table hence the modified data is not updated in QA table, might be becuase being called in the same session?
using (ISession session = iNhibernet.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
try
{
// Business Entity Saved in Tag1/Tag2 Table
session.SaveOrUpdate(l);
}
catch (Exception ex)
{
ErrorLogExceptionHandler.ErrorLog(ref ex);
throw new Exception("Unable to save data");
}
// Calling Oracle Package to Compare Tag1 and Tag2 data and inserting data in QA Table.
IDbCommand db = ProductionLog.ProductionLogUpdate(l.ProductionlogSeqNo, loadAction) as DbCommand;
db.Connection = session.Connection;
try
{
db.ExecuteNonQuery();
}
catch (Exception ex)
{
ErrorLogExceptionHandler.ErrorLog(ref ex);
throw new Exception("Unable to insert in production log");
}
transaction.Commit();
}
}
Can some help.
Thanks,
You are calling
session.SaveOrUpdate(l);
but this does not force the session to instantly update the database.
NHibernate minimises calls to the database by storing all pending updates, and then calling them on the database in one batch. The SaveOrUpdate invocation is instructing the session to update the database with changes to l on the next Flush or Commit.
The oracle command is expecting the data to have already been written to the database.
I believe you can solve this problem with an explict call to Flush.
using (ITransaction transaction = session.BeginTransaction())
{
try
{
// Business Entity Saved in Tag1/Tag2 Table
session.SaveOrUpdate(l);
session.Flush(); // <<== Write all of our changes so far
}
catch (Exception ex)
{
ErrorLogExceptionHandler.ErrorLog(ref ex);
throw new Exception("Unable to save data");
}

Repository.SaveOrUpdate(), certainty of rollback executed on exception?

For saving a list of (virtual) bank account transactions, I want the business entity to reflect the state saved to database, also in case of an exception.
Can I assume that an exception here also means the transaction is rolled back? Or can I explicitly rollback in the catch to be sure? If so, what if that line throws an exception?
In Repository< T >:
public void SaveOrUpdate(IList<T> entityList)
{
using (ISession session = FluentNHibernateManager.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
try
{
foreach (T entity in entityList)
session.SaveOrUpdate(entity);
transaction.Commit();
}
catch (Exception e)
{
MyTrace.Exception(e.ToString());
// add this line? transaction.Rollback();
throw;
}
}
}
}
In Some Class:
cashTransactions.Add(t);
try {
GenericRepository<CashTransaction> repo = new GenericRepository<CashTransaction>();
repo.SaveOrUpdate(cashTransactions);
} catch (Exception ex) {
cashTransactions.Remove(t);
}
You can't assume that the transaction was rolled back, but you don't have to assume: ITransaction has a bool WasCommitted property.
You can check that to determine whether the transaction was committed, and call Rollback() explicitly, where warranted.
You've gotta include the rollback() call to rollback the transaction properly.
To dispose a not-commited transaction will always rollback it.
This is true in all ado.net transactions implementation, and of course at runtime NHibernate will use your chosen ado.net provider.

Categories