How to log and intercept all database operations using Entity Framework? - c#

I have the flowing transaction code :
using (var context = new StDbContext())
{
context.Database.Log = Console.Write;
if (!context.Database.Connection.State.HasFlag(ConnectionState.Open))
{
context.Database.Connection.OpenAsync();
}
using (var transaction = context.Database.BeginTransaction())
{
try
{
var unit_of_work = new UnitOfWork(context);
unit_of_work.Sessions.AddModelSession(Model, unit_of_work);
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
TestBaseCore.mNLogLoggerService.Error(ex.Message);
}
}
}
Inside unit_of_work.Sessions.AddModelSession(); I'm saving data for different tables (UnitOfWork.Project.AddProject(); .... UnitOfWork.Fixture.AddFixture() ..and so on)->but sometimes the code is crashing in `unit_of_work.Sessions.AddModelSession();' and I'm not able to catch any exception using try catch?
Is there a way to log in a log file all the database operations to be able to figure it out whats the real issue there?

Related

LINQ to SQL not creating Transactions

I've been searching everywhere, to try and get over this issue but I just can't figure this out.
I'm trying to make many changes to the DB with one single transaction using LINQ to SQL.
I've created a .dbml that represents the SQL Table, then I use basicaly this code:
foreach (var _doc in _r.Docs)
{
try
{
foreach (var _E in _Es)
{
Entity _newEnt = CreateNewEnt(_EListID, _doc, _fileName, _E);
_db.Etable.InsertOnSubmit(_newEnt);
_ECount++;
if (_ECount % 1000 == 0)
{
_db.SubmitChanges();
}
}
}
catch (Exception ex)
{
throw;
}
}
But when I do a SQL Profiler, the commands are all executed individually. It won't even start an SQL Transaction.
I've tried using TransactionScope (using statement and Complete()) and DbTransaction (BeginTransaction() and Commit()), none of them did anything at all, it just keeps on executing all commands individually, inserting everything like it was looping through all the inserts.
TransactionScope:
using(var _tans = new TransactionScope())
{
foreach (var _doc in _r.Docs)
{
try
{
foreach (var _E in _Es)
{
Entity _newEnt = CreateNewEnt(_EListID, _doc, _fileName, _E);
_db.Etable.InsertOnSubmit(_newEnt);
_ECount++;
if (_ECount % 1000 == 0)
{
_db.SubmitChanges();
}
}
}
catch (Exception ex)
{
throw;
}
}
_trans.Complete();
}
DbTransaction:
_db.Transaction = _db.Connection.BeginTransaction();
foreach (var _doc in _r.Docs)
{
try
{
foreach (var _E in _Es)
{
Entity _newEnt = CreateNewEnt(_EListID, _doc, _fileName, _E);
_db.Etable.InsertOnSubmit(_newEnt);
_ECount++;
if (_ECount % 1000 == 0)
{
_db.SubmitChanges();
}
}
}
catch (Exception ex)
{
throw;
}
}
_db.Transaction.Commit();
I also tried commiting transactions everytime I Submit the changes, but still nothing, just keeps on executing everything individually.
Right now I'm at a loss and wasting time :\
GSerg was right and pointed me to the right direction, Transactions do not mean multiple commands in one go, they just allow to "undo" all that was made inside given transaction if need be. Bulk statements do what I want to do.
You can download a Nuget Package directly from Visual Studio called "Z.LinqToSql.Plus" that helps with this. It extends DataContext from LINQ, and allows to do multiple insertions, updates or deletes in bulks, which means, in one single statement, like this:
foreach (var _doc in _r.Docs)
{
try
{
foreach (var _E in _Es)
{
Entity _newEnt = CreateNewEnt(_EListID, _doc, _fileName, _E);
_dictionary.add(_ECount, _newEnt); //or using a list as well
_ECount++;
if (_ECount % 20000 == 0)
{
_db.BulkInsert(_dictionary.Values); //inserts in bulk, there are also BulkUpdate and BulkDelete
_dictionary = new Dictionary<long, Entity>(); //restarts the dictionary to prepare for the next bulk
}
}
}
catch (Exception ex)
{
throw;
}
}
As in the code, I can even insert 20k entries in seconds. It's a very useful tool!
Thank you to everyone who tried helping! :)

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

Entity framework 6 transaction

I need to insert new rows in two of my tables first table's auto generated id field is one of the field of second table .currently I'm using transaction for inserting data. My current code is given below
using (var context = new ApplicationDatabaseEntities())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
foreach (var item in itemList)
{
context.MyFirstEntity.Add(item);
context.SaveChanges();
mySecondEntity.MyFirstEntityId = item.Id;
context.MySecondEntity.Add(mySecondEntity.MyFirstEntityId );
context.SaveChanges();
}
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine(ex);
}
}
}
The above code is working fine .my question is, is this the correct way? I mean if I have 1000 or 2000 items for insertion does my current code affect the performance
Code can be improved with implicit transaction:
foreach (var item in itemList)
{
context.MyFirstEntity.Add(item);
mySecondEntity.MyFirstEntity = item;
context.MySecondEntity.Add(mySecondEntity);
}
context.SaveChanges();
Note: instead of id I've used navigation property.
It depends. If the whole batch has to be transactional then you have no other way as do in one big transaction. If transaction has to be guaranteed for tupples only then if the time of transaction is big enough you may face some locks. Then you can just do transaction in the loop for each tupple.
Also you can do what you are doing in one go without explicit transaction. You can SaveChanges after the loop and it will be transactional:
foreach (var item in itemList)
{
context.MyFirstEntity.Add(item);
mySecondEntity.MyFirstEntity = item;
context.MySecondEntity.Add(mySecondEntity);
}
context.SaveChanges();
You should wrap your transaction in a using statement and rollback when an exception is thrown.
using (DbContextTransaction transaction = context.Database.BeginTransaction())
{
try
{
foreach (var item in itemList)
{
context.MyFirstEntity.Add(item);
mySecondEntity.MyFirstEntity = item;
context.MySecondEntity.Add(mySecondEntity);
}
transaction.Commit();
}
catch (Exception e)
{
transaction.Rollback();
}
}
More info here.

fluent NHibernate session exception handling

How should NHibernate exceptions be handled during sessions ? There are many examples over the internet:
http://nhibernate.info/doc/nh/en/index.html#manipulatingdata-exceptions
https://svn.code.sf.net/p/nhibernate/code/trunk/nhibernate/src/NHibernate/ISession.cs
And many, many StackOwerflow threads that suggest an approach similar to this:
using (ISession session = factory.OpenSession())
using (ITransaction tx = session.BeginTransaction())
{
try
{
// do some work
...
tx.Commit();
}
catch (Exception e)
{
if (tx != null) tx.Rollback();
throw;
}
}
But what if an error happens and an exception is thrown ON the 1st line of code(When you're opening a session) ? None of the examples cover it up!
A colege of mine sugested this approach:
ITransaction transaction = null;
try
{
using (ISession session = databaseFacade.OpenSession())
{
transaction = session.BeginTransaction();
//do some work
...
transaction.Commit();
}
}
catch (Exception ex)
{
if (transaction != null)
transaction.Rollback();
throw new Exception(ex.Message);
}
I suggest decoupling components that
open session
perform db operations
with this approach you can keep logic for handling OpenSession() exceptions inside your 1st line and don't worry later. Reason is that if (as in your case) databaseFacade.OpenSession() throws exception you don't have to catch it and check for transaction since it must be null
//if OpenSession() throws it's fine , not transaction at all
using (ISession session = databaseFacade.OpenSession())
{
using (ITransaction tx = session.BeginTransaction())
{
try
{
// do some work
...
tx.Commit();
}
catch (Exception e)
{
//tx is not null at this point
tx.Rollback();
throw;
}
}
}

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

Categories