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

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");
}

Related

Create EF db instance for every insert?

I have a worker thread, whose job is to insert Objects that are stored in a Queue into the database.
We are currently using Entity framework to do the inserts. Now my question is, do I need to make a new Db Instance for every insert? or can I safely re-use the same db instance over and over?
private static void MainWorker()
{
while (true)
{
try
{
if (IncomingDataQueue.Any())
{
if (IncomingDataQueue.TryDequeue(out var items))
{
//Insert into db
using (var db = GetNewDbInstance())
{
if (db != null)
{
db.DataRaw.AddRange(items);
db.SaveChanges();
//Skip everything and continue to the next loop
continue;
}
}
}
}
}
catch (Exception ex)
{
Debug.WriteException("Failed to insert DB Data", ex);
//Delay here in case we are hitting the db 2 hard.
Thread.Sleep(100);
}
//Wait here as we did not have any items in the queue, so wait before checkign again
Thread.Sleep(20);
}
}
Here is my function which gets a new DB Instance:
private static DbEntities GetNewDbInstance()
{
try
{
var db = new DbEntities();
db.Configuration.ProxyCreationEnabled = false;
db.Configuration.AutoDetectChangesEnabled = false;
return db;
}
catch (Exception ex)
{
Debug.WriteLine("Error in getting db instance" + ex.Message);
}
return null;
}
Now I have not had any issues to date, however, I worry that this solution will not scale well if we are for example doing 1000s of inserts per minute?
I then also worry that with 1 static db instance that we could get memory leaks or that object will keep growing and not manage it's db connections properly?
What is the correct way to use EF with long term db connections?

Attach an object to a session

I'm trying to fix a bug in some legacy code. The code is trying to delete an object (a row) from the database. The Session, however, sometimes does not contain the object and throws an exception. I've added the test to see if the Session contains the object and then try various things to get the object into the Session, but no success thus far. Can you suggest what else I can try?
This code has implemented nHibernate Session very poorly in that it does not follow the Unit of Work ethic. The application creates one session when it starts and uses that session throughout the life of the application. Nothing I can do about this (other than a complete rewrite, in which case I would abandon nHibernate anyway).
using (ITransaction transaction = this.Session.BeginTransaction())
{
try
{
if (Session.Contains(object))
{
Session.Delete(object);
transaction.Commit();
}
else // new code, Session does not contain object
{
try
{
//Session.Close();
//Session.Dispose();
//Session.Disconnect();
Session.Merge(object);
Session.Save(object); // this does something
Session.Lock(object, LockMode.Upgrade); // .Write in invalid mode
Session.Update(object);
using (ITransaction transaction2 = this.Session.BeginTransaction())
{
// this code executes with no error but does not delete the row from the database
Session.Delete(object);
transaction2.Commit();
}
}
catch (System.Exception exception)
{
string message2 = exception.ToString();
MessageBox.Show(message2, "Delete() Try it again ", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
catch (System.Exception exception)
{
if (transaction != null)
{
transaction.Rollback();
}
throw exception;
}
}
Why don't you get the object from the session first?
using (ITransaction transaction = Session.BeginTransaction())
{
var persisted = Session.Get<SomeType>(object.Id); //object will be loaded if not already in the session
Session.Delete(persisted);
transaction.Commit();
}

BeginTransaction on localdb

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.

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.

How use Transaction in EntityFramework 5?

I want to trancate some table same time.
If one not success, must be all rolback.
Something like that:
ctx.Database.ExecuteSqlCommand("truncate table tb_expensesall");
ctx.Database.ExecuteSqlCommand("truncate table tb_wholesale");
ctx.Database.ExecuteSqlCommand("truncate table tb_singlesale");
ctx.Database.ExecuteSqlCommand("truncate table tb_purchase");
But the problem is , I dont know how use transaction for this.
I trying this:
using (gasstationEntities ctx = new gasstationEntities(Resources.CONS))
{
ctx.Database.Connection.Open();
DbTransaction tr = ctx.Database.Connection.BeginTransaction();
try
{
ctx.Database.ExecuteSqlCommand("truncate table tb_expensesall");
ctx.Database.ExecuteSqlCommand("truncate table tb_wholesale");
ctx.Database.ExecuteSqlCommand("truncate table tb_singlesale");
ctx.Database.ExecuteSqlCommand("truncate table tb_purchase");
//commit the transaction
tr.Commit();
new MessageWindow(this, Resources.GetString("Warn"), Resources.GetString("DeleteSuccess"));
}
catch (Exception ex)
{
//return
tr.Rollback();
}
//close
ctx.Database.Connection.Close();
}
The problem here: tr.Commit();
and the Exception tell me :
{System.InvalidOperationException: Connection must be valid and open to rollback transaction
And the tr.Rollback(); throw exception to.
the exception is:
{System.InvalidOperationException: Connection must be valid and open to rollback transaction
The realy funy thing is , the table truncate is success. what? the commmit is throw exception . and it can be success? i can't understand.
Please tel me what is goning on . if you give me a solution, that's even better.
Add reference to System.Transactions, import using System.Transactions; and then try to encapsulate your code by
using (gasstationEntities ctx = new gasstationEntities(Resources.CONS))
{
using (var scope = new TransactionScope())
{
[... your code...]
scope.Complete();
}
}
If exception occurs, scope.Complete() is not called and the rollback is automatic.
EDIT : I've just seen your MySql tag. If this doesn't work, have a look here !
Try this,
Technically, the using should commit the transaction when there are no exceptions, but in case of exception, the using will automatically rollback it.
using (var txn = new TransactionScope())
{
ctx.Database.ExecuteSqlCommand("truncate table tb_expensesall");
ctx.Database.ExecuteSqlCommand("truncate table tb_wholesale");
ctx.Database.ExecuteSqlCommand("truncate table tb_singlesale");
ctx.Database.ExecuteSqlCommand("truncate table tb_purchase");
txn.Complete();
}
new MessageWindow(this, Resources.GetString("Warn"), Resources.GetString("DeleteSuccess"));

Categories