I have a problem that when root transaction scope dispose the nested completed transactions scopes not rolled back.
I need to rollback all nested transaction scopes when dispose the root.
the root scope from the root wcf service
using (var ts = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted, Timeout = new TimeSpan(0, 10, 0) },
TransactionScopeAsyncFlowOption.Enabled))
{
try
{
//logic ... and call the inner scope
ts.Complete();
return result;
}
catch (Exception ex)
{
ts.Dispose();
throw ex;
}
}
the nested scope in another wcf service
using (var ts = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted, Timeout = new TimeSpan(0, 10, 0) },
TransactionScopeAsyncFlowOption.Enabled))
{
try
{
//logic
ts.Complete();
return result;
}
catch (Exception ex)
{
ts.Dispose();
throw ex;
}
}
I need when the root scope disposed as a result of exception, roll back all inner transaction scopes
Related
When the code is working and it reaches the part where it performs a Commit, it is where the error appears
I have seen solutions where a modification must be made to the Webconfig in the appsettings section, however, is there no other solution that does not have to do with modifying the Webconfig?
This is the code where the error appears, exactly in the Commit
public ErrorCode insertNC(EntidadesNC data)
{
var errorCode = new ErrorCode();
DbConnection DataConnection = ConnectionGet(enuTypeDataBase.OracleVTime);
DbTransaction tran = null;
try
{
DataConnection.Open();
tran = DataConnection.BeginTransaction();
foreach (var item in data.ListainsertNC)
{
if (errorCode.P_COD_ERR == 0)
{
errorCode = insertNCV2(item, DataConnection, tran);
}
}
}
catch (Exception ex)
{
errorCode.P_COD_ERR = 1;
errorCode.P_MESSAGE = ex.ToString();
ELog.save(this, ex.ToString());
}
finally
{
if (errorCode.P_COD_ERR == 0)
{
tran.Commit();
}
else
{
tran.Rollback();
}
if (DataConnection.State == ConnectionState.Open) DataConnection.Close();
}
return errorCode;
}
This is the code where the function insertNCV2 is called
public ErrorCode insertNCV2(itemNC data, DbConnection connection, DbTransaction tran)
{
List<OracleParameter> parameter = new List<OracleParameter>();
var response = new itemNC();
var errorCode = new ErrorCode();
var sPackageName = ProcedureName.pkg_paymentNC + ".SP_PRUEBA_PAYROLL";
try
{
parameter.Add(new OracleParameter("P_NID_COTIZACION", OracleDbType.Int64, data.P_NID_COTIZACION, ParameterDirection.Input));
parameter.Add(new OracleParameter("P_NRECEIPT", OracleDbType.Varchar2, data.P_NRECEIPT, ParameterDirection.Input));
parameter.Add(new OracleParameter("P_SCODCHANNEL", OracleDbType.Int64, data.P_SCODCHANNEL, ParameterDirection.Input));
//OUTPUT
OracleParameter P_COD_ERR = new OracleParameter("P_COD_ERR", OracleDbType.Int32, 9000, errorCode.P_COD_ERR, ParameterDirection.Output);
OracleParameter P_MESSAGE = new OracleParameter("P_MESSAGE", OracleDbType.Varchar2, 900, errorCode.P_MESSAGE, ParameterDirection.Output);
parameter.Add(P_COD_ERR);
parameter.Add(P_MESSAGE);
OracleDataReader odr = (OracleDataReader)this.ExecuteByStoredProcedureVT(sPackageName, parameter);
// ELog.CloseConnection(odr);
}
catch (Exception ex)
{
errorCode.P_COD_ERR = 1;
errorCode.P_MESSAGE = "Hubo un error al insertar los sub items de las coberturas"; // ex.ToString();
ELog.save(this, ex.ToString());
}
return errorCode;
}
I am thankful for any kind of help :)
InvalidOperationException with database operations generally indicates that the connection has been closed or aborted before your line of code executes.
The other scenario with transactions is if the transaction was never started. With Transactions things are a bit easier, we don't need to rollback a transaction on an exception, many exceptions raised during DB operations will abort the transaction anyway, closing the connection without an explicit Commit will also effectively rollback the transaction.
Use Rollback to revert back to a transaction check point, not to cancel or abort the transaction.
Moving the commit logic might help you to debug this:
public ErrorCode insertNC(EntidadesNC data)
{
var errorCode = new ErrorCode();
DbConnection DataConnection = ConnectionGet(enuTypeDataBase.OracleVTime);
DbTransaction tran = null;
try
{
DataConnection.Open();
tran = DataConnection.BeginTransaction();
foreach (var item in data.ListainsertNC)
{
if (errorCode.P_COD_ERR == 0)
{
errorCode = insertNCV2(item, DataConnection, tran);
}
}
if (errorCode.P_COD_ERR == 0)
{
tran.Commit();
}
else
{
tran.Rollback();
}
}
catch (Exception ex)
{
errorCode.P_COD_ERR = 1;
errorCode.P_MESSAGE = ex.ToString();
ELog.save(this, ex.ToString());
}
finally
{
if (DataConnection.State == ConnectionState.Open) DataConnection.Close();
}
return errorCode;
}
I am getting Error : The transaction associated with the current connection has completed but has not been disposed. after Log.WriteEntry($"Error occurred while trying to create invoice for transaction {transaction.TransactionLogId} : {ex.Message}"); statement when i run the code.. Can someone please help?
public virtual GroupCreationResult CreateGroups(IEnumerable<TransactionDetail> transactions)
{
var transactionDetails = transactions as TransactionDetail[] ?? transactions.ToArray();
var successes = new List<int>(transactionDetails.Length);
var errors = new List<TransactionError>();
foreach (var transaction in transactionDetails)
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = new TimeSpan(0, 2, 0) }))
{
try
{
foreach (var service in _invoiceCreationServices)
{
try
{
service.CreateInvoice(transaction);
}
catch (Exception ex)
{
Log.WriteEntry($"Error occurred while trying to create invoice for transaction {transaction.TransactionLogId} : {ex.Message}");
if (!(ex.ToString().Contains("Violation of PRIMARY KEY constraint 'PK_APInvGrp'.") || ex.ToString().Contains("Violation of PRIMARY KEY constraint .")))
{
Log.WriteEntry($"error occured while adding the transaction {transaction.TransactionLogId} - {ex.ToString()}");
errors.Add(new TransactionError(transaction.TransactionLogId, ex.ToString()));
scope.Complete();
break;
}
}
}
Log.WriteEntry($"successfully added the transaction {transaction.TransactionLogId}");
successes.Add(transaction.TransactionLogId);
scope.Complete();
}
catch (Exception exception)
{
Log.WriteEntry($"error1 occured while adding the transaction {transaction.TransactionLogId} - {exception.ToString()}");
//errors.Add(new TransactionError(transaction.TransactionLogId, exception.ToString()));
}
}
}
return BuildGroupCreationResult(successes, errors);
}
I have question regarding Entity Framework. In my program I first fill-up my DbContext with data. Then I need to delete all the data from the tables in DB, but only if new data will be saved. If db.Savechanges() throws an exception I need my old data to still be in the tables.
My code is:
static void Main(string[] args)
{
PdmContext db = new PdmContext();
FillDbContext();
try
{
if (db.SaveChanges() > 0)
{
using (var del = new PdmContext())
{
DeleteModel.deleteFromAllTables();
}
db.SaveChanges();
}
}
catch (Exception exp)
{
Logger.Log("Exception (global catch));
}
}
I can't seem to figure this out. Anyone can help with this? :)
You can use Transaction which will make sure to revert the operation done within the scope of it if the operation fails at some stage :
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var del = new PdmContext())
{
DeleteModel.deleteFromAllTables();
}
db.SaveChanges();
scope.Complete(); // commits the transaction
}
Now the changes to the database will be atomic so that it will only keep all changes or not at all. I have not included exception handling code for simplicity but due to any reason if the scope.Complete() was not being executed and control exists the transaction block without executing that the transaction will get rolled back.
You need to use a Transaction.
see how to use that:
using (var dbContextTransaction = PdmContext.Database.BeginTransaction())
{
try
{
// HERE your operation insert etc.
PdmContext.SaveChanges();
dbContextTransaction.Commit(); // here, apply your operation
}
catch (Exception)
{
dbContextTransaction.Rollback(); // here, undo your operations
}
}
You can handle such scenario with transaction management.
There is two way to handle it.
1) You can use single dbcontext for all operation instead of create multiple for single operation.
using (var context = new SchoolContext())
{
try
{
context.Students.Add(new Student()
{
FirstName = "Rama2",
StandardId = standard.StandardId
});
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Error occurred.");
}
}
2) Using single DbContextTransaction object:
using (var context = new SchoolContext())
{
context.Database.Log = Console.Write;
using (DbContextTransaction transaction = context.Database.BeginTransaction())
{
try
{
context.Students.Add(new Student()
{
FirstName = "Rama2",
StandardId = standard.StandardId
});
context.SaveChanges();
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Error occurred.");
}
}
}
I hope it work for you.
i getting this error message from my transaction code, and there is no other transaction uncompleted
The transaction operation cannot be performed because there are
pending requests working on this transaction.
TransactionScope tran = null;
using (tran = new TransactionScope())
{
try
{
string resNumber = repoKontrat.KontratNoUret(kontratTipi); //procedure
_seciliKontrat.ID = repoKontrat.KaydetID(seciliKontrat);//insertOnSubmit()
repoKontratKalem.KaydetProc(
_seciliKontrat.ID,
await repoKontratKalem.Getir_Table(gridKontratKalemler.ToList()),
digerKalemleriDegistir //bool
); // procedure
gp.Kaydet(_seciliKontrat.ID, gp.KontratGpTahminiveGercekHesapla(_seciliKontrat.ID));//insertOnSubmit()
gp.Kaydet(_seciliKontrat.ID, gp.KontratGpTahminiveGercekHesapla(_seciliKontrat.ID));//procedure
repoKontrat.SatisKontratiFirmaBilgileriniGuncelle(_seciliKontrat.ID);//procedure
//-----
foreach (var item in silinenKasaKayitlari)
{
repoCariHareketler.Sil(Convert.ToInt32(item));//insertOnSubmit()
}
repoCariHareketler.Kaydet(_seciliKontrat.ID, await repoCariHareketler.Getir_Table(gridKontratKasa.ToList()));//insertOnSubmit()
tran.Complete();
}
catch (Exception ex)
{
throw ex;
}
finally
{
tran = null;
}
lib.ParamsInsert is called via another dll.
The transaction is rolled back when there is a problem with the throw statement. However, the operations in paramsInsert can not be undone.
In lib.ParamsInsert, there is a transaction in its own error.
spcarikart.Repository lib = new spcarikart.Repository();
using (var transaction = db.Database.BeginTransaction())
{
try
{
var result = db.Acenta.Add(obj).Entity;
var a = lib.ParamsInsert(db, new Params
{
Baslik = "Bahdir",
Deger = "1"
});
// ....Maybe Error other process
db.SaveChanges();
return result;
}
catch (Exception ex)
{
transaction.Rollback();
}
}
ParamsInsert
using (var transaction = db.Database.BeginTransaction())
{
try
{
var resul = db.Params.Add(obj).Entity;
db.SaveChanges();
transaction.Commit();
return resul;
}
catch (Exception ex)
{
transaction.Rollback();
throw new Exception();
}
}
They use different transactions. Don't start new transaction, as I see, you work with the same db context, so needn't start transaction in paramsInsert, remove using (var transaction = db.Database.BeginTransaction()) from it.