NHibernate: Multiple Transactions on a single session - c#

I want to know if there is any issue by creating several transactions on a single session, like this:
using (var session = factory.OpenSession())
{
using (var trans1 = session.BeginTransaction())
{
.....
trans1.commit();
}
using (var trans2 = session.BeginTransaction())
{
.....
trans2.commit();
}
using (var trans3 = session.BeginTransaction())
{
.....
trans3.commit();
}
using (var trans = session.BeginTransaction())
{
.....
// trans1.commit();
}
}
is that possible or must I open a new session object per transaction?
Thanks for your help.

Yes what you are doing is just fine.
What nhibernate does not support are multiple nested transactions.

It is not usual to have multiple transactions on a single session even in NHibernate. Also from personal experience I can say it is not a good idea to reuse sessions in any other situation.
I recommend to keep the workflow as simple as possible to avoid any side-effects:
open session
open transaction
use your entities
commit transaction
rollback in case of exception
close/flush session (always)

The most common strategy in a web environment is to have session per request.
When it cones to transactions, it really depends on your use-case.
What you are doing is fine, or otherwise nhibernate wouldn't separate session from transaction.
but yet again, it depends on your business case situation.
I suggest you to wrap the session.BeginTransaction() with IDisposable, so on Dispose you make sure to commit transaction .

No issues although it's unusual. Bear in mind that the session should be discarded if any of the transactions rolls back.

Related

Trade-offs between longer data context or transaction outside of context?

I am writing a Web API Rest service that does operations on a distinct set of entities. I have broken them down like this:
db = new DBEntities();
using (var dbContextTransaction = db.Database.BeginTransaction())
{
try
{
ProcessClient();
ProcessClientPerson();
ProcessGuardian();
ProcessAddress();
ProcessEmail();
ProcessPhones();
ProcessChildren();
dbContextTransaction.Commit();
}
catch (Exception ex)
{
dbContextTransaction.Rollback();
etc.
Following the advice that data contexts should live as short as possible, each of the methods creates its own data context, calls SaveChanges(), and disposes of it at the end:
private ProcessClient()
{
db = new DBEntities();
....
This obviously does not work - a transaction context created this way is tied to the data context. If something goes wrong in one of the entity operations, only that operation is rolled back (implicitly), but the overarching transaction is not.
I found this approach for creating a transaction outside of EF, but I am wondering if I should follow it or if I should just let my data context live for the duration of the transaction and keep the transaction inside of EF!?
I am not looking for an opinion, but for data around stability, performance, etc.
There is no immediate need to keep contexts short-lived. You can do that but you don't have to.
Over time entities will accumulate in a context. If you risk running out of memory it can be necessary to let go of a context.
Otherwise, the usual procedure is to keep the context alive for the duration of the logical unit of work. Here, that UOW is all those methods in their entirety.
This also makes transaction management easier (as you already found out).
dbContextTransaction.Rollback();
This is an anti-pattern. Simply don't commit in case of error.
I have mixed feelings about this. I am working against a legacy database that has no foreign key constraints, and I am inserting, updating, and deleting between 20 and 30 objects in one of these service calls.
The problem is that I need to call SaveChanges() frequently to get the identity column values that will become foreign keys.
On the other hand, I have to be able to roll back everything if there is a problem three layers down, so a single large transaction is needed.
For some reason that I have not been able to determine, calling SaveChanges repeatedly on the same data context would result in errors that the connection state is open. So I ended up giving each method its own data context anyway:
var scope = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted);
using (scope)
{
try
{
ProcessClient();
ProcessClientPerson();
ProcessGuardian();
ProcessAddress();
ProcessEmail();
ProcessPhones();
ProcessChildren();
scope.Complete();
}
catch (System.Data.Entity.Validation.
DbEntityValidationException ex)
{
[...] handle validation errors etc [...]
}
}
with each section doing basically this, once stripped down to the bare essentials:
private void ProcessClient() {
{
using (MyDBEntities db = new MyDBEntities())
{
[...] doing client stuff [...]
aClient.LastUpdated = DateTime.Now;
db.AddOrUpdate(db, db.Clients, aClient, aClient.ClientID);
db.SaveChanges();
ClientId = aClient.ClientID; // now I can use this to form FKs
}
}
Mixed feelings about locking, because on my development VM the transaction runs for 1-2 seconds and this is a production database with office staff and online customers doing CRUD transactions through web applications at the same time.
Unrelated, but helpful for my AddOrUpdate method was this blog post.

EF6 transaction error in ObjectContext

please can someone help i'm trying to use Transaction in EF6. it's my first try in transaction in .net.
i have read the manual by Microsoft in their site.
http://msdn.microsoft.com/en-us/data/dn456843.aspx and i would to use a transaction for user registration but nothing is working. Im using EF objectContext.
here is a little piece of my code :
using (var context= new MyModelContainer())
{
using(var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
// code here to create user
context.savechanges();
// code here to add role
context.savechanges();
dbContextTransaction.Commit();
}
catch
{
dbContextTransaction.Rollback();
}
}
}
My problem is that Visual Studio does not even recognize Database.BeginTransaction(). may be because we are using objectContext? i never use transaction. i change to use another database where our model is DbContext it's seems to work.
How can we use objectContext with transaction also (mean not distributed systems) ?
any tutorial please ?
I tried transaction scope but it's seem to work but i read that it's for distributed system ((( it's means that performance going down. any suggestion ?
thanks for your time !!!
Replace the line
using(var dbContextTransaction = context.Database.BeginTransaction())
with this:
using(var dbContextTransaction = new TransactionScope())
and remove the call to Rollback. TransactionScore will do what you need.
TransactionScope is compatible with distributed systems, but if you do not make a call to other db's and use it as described, then it is more than good enough for your needs. If you try to make a call to save to another db (for example) then it will require special privileges and an exception will be thrown.

EF using and BeginTransaction, when to call Connection.Close()?

After opening a connection, Do I have to explicitly close it? or does .net close it automatically? What about if an exception is thrown?
using (DataContext pContext = new DataContext())
{
pContext.Connection.Open()
using (var trx = pContext.Connection.BeginTransaction())
{
//make changes to objects
//Make calls to ExecuteStoreCommand("Update table SET pee=1 AND poop=2 WHERE ETC");
pContext.SaveChanges();
trx.Commit();
}
}
Does pContext.Connection.Close()` get called in all instances by the framework? I know that in a normal using statement it does, but what about when I manually open it?
I am using the Connection.BeginTransaction because In my tests, (using SaveChanges() alone without transactions) if this code executes and fails for some reason, the commands sent during ExecuteStoreCommand() are saved even though the changes to my objects aren't.
I have to call Connection.Open() before calling pContext.Connection.BeginTransaction() otherwise I get the error:
Inner Exception:
The connection is not open.
Any help would be appreciated.
There is no need to call DataContext.Connection.Open(). In fact, it's bad practice to do so.
The context opens a connection only when it needs to. By calling Connection.Open, you open the connection for far longer that is needed, increasing the chance that locks will accumulate and lead to blocking. It is also a good way to exhaust the connection pool in high traffic systems.
SaveChanges opens a connection and a transaction itself, so there is no need to manually open the connection or start the transaction. From the documentation:
SaveChanges operates within a transaction. SaveChanges will roll back that transaction and throw an exception if any of the dirty ObjectStateEntry objects cannot be persisted
Explicitly using connections and transactions makes sense only if you want to mix raw SQL commands inside the EF session. By itself this is not a good practice, but if you do, you don't need to close the connection because the DataContext will close the connection when it is disposed. There is a sample in the documentation, at How to: Manually Open the Connection from the Object Context
You can also handle a transaction using a TransactionScope, as described in How to: Manage Transactions in the Entity Framework. In this case, you don't need to open the connection as all changes are automatically enlisted in the current transaction. This is described in http://msdn.microsoft.com/en-us/library/bb738523(v=vs.100).aspx and an example would be :
using (DataContext pContext = new DataContext())
{
using (TransactionScope transaction = new TransactionScope())
{
//execute commands
pContext.SaveChanges();
transaction.Complete()
}
}
In any case, manually handling connections and transactions is not trivial. You should read Managing Connections and Transactions for some of the things you need to be aware
This means
using (DataContext pContext = new DataContext())
that pContext is disposed after leaving using
it's equivalent to:
DataContext pContext = new DataContext()
try
{
//do stuff here
}
finally
{
if(pContext!=null)
((IDisposable)pContext).Dispose();
}
so answer is -> you don't need to call close after your code because
Connections are released back into the pool when you call Close or Dispose on the Connection..."
from here

How to prevent EntityFramework deadlock when concurrently running these two statements

Calls into my web service use the following code to ensure that the caller has a valid session. If a valid session found then it updates the session details and saves the changes. All simple enough and works fine.
// Create the Entity Framework context
using(MyContext ctx = CreateMyContext())
{
// Get the user session for the client session
UserSession session = (from us in context.UserSessions.Include("UserEntity")
where us.SessionId = callerSessionId
select us).FirstOrDefault<UserSession>();
if (session == null)
return false;
else
{
// Update session details
session.Calls++;
session.LastAccessed = DateTime.Now.Ticks;
Console.WriteLine("Call by User:{0}", session.UserEntity.Name);
// Save session changes back to the server
ctx.SaveChanges();
return true;
}
}
All works fine until the same caller, and hence the same session, makes multiple concurrent calls (which is perfectly valid to happen). In this case I sometimes get a deadlock. Using SQL Server Profiler I can see the following is happening.
Caller A performs the select and acquires a shared lock on the user session. Caller B performs the select and acquires a shared lock on the same user session. Caller A cannot perform its update because of Caller B's shared lock. Caller B cannot perform its update because of caller A's shared lock. Deadlock.
This seems like a simple and classic deadlock scenario and there must be a simple method to resolve it. Surely almost all real world applications have this same problem.But none of the Entity Frameworks books I have mention anything about deadlocks.
I found an article that talks about this HERE. It basically sounds like you can start and stop a transaction that surrounds your EF call... The block gives the following code example so credit goes to Diego B Vega... The blog post also links to another blog with additional information.
using (var scope = new TransactionScope(TransactionScopeOption.Required, new
TransactionOptions { IsolationLevel= IsolationLevel.Snapshot }))
{
// do something with EF here
scope.Complete();
}
Will the following work for you?
using(MyContext ctx = CreateMyContext())
{
ctx.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
// Get the user session for the client session
...
}
You probably have each session using a transaction maybe? in which case they will deadlock as the transactions both try to upgrade from shared lock to exclusive lock when they attempt to save. This seems to be not well documented since EF favours optimistic concurrency.
One way out of this is to provide an updatelock hint using something like this:
return context.TestEntities
.SqlQuery("SELECT TOP 1 Id, Value FROM TestEntities WITH (UPDLOCK)")
.Single();
}
see: entity framework 6 and pessimistic concurrency

How can i enable Transaction my codes with linqto SQL?

if writing below codes: Error returns.i do like advise : http://stackoverflow.com/questions/794364/how-do-i-use-transactionscope-in-c
But only error change:The partner transaction manager has disabled its support for remote/network transactions Exception from HRESULT: 0x8004D025
i am using windows server 2003.
using (var stockMovementCtx = new StockMovementCtxDataContext())
{
using (var scope = new TransactionScope())
{
// do something....
}
scope.Complete();
}
but if i changed my codes ; every thing is ok:
using (var stockMovementCtx = new StockMovementCtxDataContext())
{
// do something....
}
How can i solve below error. This is really important.Please help me:((((
TransactionScope will elevate to DTC if necessary. It sounds like DTC is not correctly configured, usually due to firewall restrictions. Try a dtcping between the servers (important: in both directions).
DataContext by default wraps all operations within a Transaction, so you don't need to explicitly do Transaction while working with DataContext. Read this.
http://weblogs.asp.net/scottgu/archive/2007/07/11/linq-to-sql-part-4-updating-our-database.aspx
using (var stockMovementCtx = new StockMovementCtxDataContext())
{
// do something....
//everything until this point is within the transaction.
stockMovementCtx.SubmitChange();
}
Why we need TransactionScope ?
TransactionScope enables you to perform transactions beyond dataabse. Say you have series of operations as below and all are atomic and they need be performed within a transaction.
1.) Add record in Table 1
2.) Add record in Table 2
3.) Write a NEW file on the Disk
4.) Rename file X on the disk
5.) Update file record in Table 3
If you use SqlTransaction then only the operaration 1,3 and 5 can participate in the transaction but 3 and 4 cannot because they do not relate database at all. In such cases TrasanctionScope can help you. TransactionScope leverages the MSDTC (Distributed Trasanction co-coordinator) that can help you do transactions beyond database context. You can wrap all five operations within a TransactionScope transaction execute them atomically. It is also worth noting that TransactionScope supports nested transactions, that means one block of transaction can contain multiple set of transactions.

Categories