NHibernate session/transaction per request preventing multiple inserts - c#

The MVC 3 project I am working on commits/rolls back whatever insert/update calls I make at the end of the current session. This works fine in most cases except for now where I am doing multiple inserts.
When a csv is uploaded to the site, each record is parsed and inserted into the DB. The problem is, if one record fails to insert, all previous records get rolled back and all subsequent attempts fail with the error:
don't flush the Session after an exception occurs
How can I disable the request-per-session (or do I have to "restart" the transaction somehow)?
EDIT: Details
The session per request stuff is configured in a class that implements IHttpModule. This class takes an HttpApplication context and UnitOfWork class. This is where the commits and rollbacks occur.
The UnitOfWork, mentioned above, is injected into the repositories using StructureMap.
Like I said, this behavior is fine for 99% of the rest of the site, I just need to get this bulk-update to ignore session-per-request transactions. Or I need to manually restart a transaction for each new record. I just don't know how.

ISession has FlushMode property with default value Auto,
Auto The ISession is sometimes flushed before query execution in order to ensure that queries never return stale state. This is the default flush mode.
Commit The ISession is flushed when Transaction.Commit() is called
Never The ISession is never flushed unless Flush() is explicitly called by the
application. This mode is very efficient for read only transactions
Unspecified Special value for unspecified flush mode.
Always The ISession is flushed before every query. This is almost always unnecessary and inefficient.
Try change ISession's FlushMode property on Commit value.

The session per request stuff is configured in a class that implements IHttpModule.
That session-per-request that you start in the HttpModule is something you do, not NHibernate. How to disable it, depends on your code. I personally don't like abstracting NHibernate in some UnitOfWork-class, because now you realize that the abstraction you used isn't good enough and a dirty hack is probably the only way to go now.
What you actually would like to do (and is not recommended) is:
foreach (var row in rows)
{
using (var session = SessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
var whatever = ...
session.Save(whatever);
tx.Commit();
}
}

Related

NHibernate, Get<TDto>(TId id) - should transaction begin or not?

I am kind of new to the NHibernate... should I ever .BeginTransaction() in order to get item? Without it code throws, however with it looks ugly since no tx.Commit()/.CommitAsync() called explicitly. I assume end of IDisposable will do?
public override async Task<TDto> Get(int id)
{
using (var sessionBuilder = NHibernateConfiguration.Instance.BuildSessionFactory())
using (var session = sessionBuilder.OpenSession())
using (var tx = session.BeginTransaction())
{
return await session.GetAsync<TDto>(id);
}
}
It is recommended to wrap each call (individual or multiple calls) in transaction. This helps generating reliable results in case of multi-threaded/multi-process environment when other thread/process alter the data you are reading. The call also include Read call.
Other reason why it is recommended is because modern RDBMS always wrap each call in transaction if not already done. It costs. Better if programmer already know the Unit Of Work, that cost can be saved and also calls can be grouped far better.
But, it may not be necessary for any (Create, Read, Update, Delete) call depending on scenario. It is per case decision. If you avoiding doing this due to "code looks ugly" reason, please consider implementing UoW.
By the way, I hope you are not calling BuildSessionFactory for each call. That is costly and more importantly, not needed. Call it at startup of your application instead.
In response to comment:
I mean same as mentioned in comment by "Oskar Berggren"; its just not explicit enough. As question is more about transaction and UoW is more than just transaction, I preferred to just "mention" the UoW than going into details.
With reference to first paragraph, "multiple calls" could be enclosed in single Unit Of Work. Please understand that UoW is more than transaction. With ORM like NHibernate, use of UoW makes far better use of transaction. Also note that with full ORM, you do not need to do too much to implement UoW. ISession itself is UoW. You just need to scope-use-manage it properly.
Following answers may be helpful to you:
https://stackoverflow.com/a/51781877/5779732
https://stackoverflow.com/a/50721461/5779732

Can an async function run at the same time as another instance of it is running?

So I have a function like this in a singleton service that is injected into a controller.
public async Task<ResponseModel> Put(BoardModel request)
{
var board = await dbService.GetBoardAsync(request.UserId, request.TargetId, request.Ticker);
// Update the model
// ...
var response = await dbService.SetBoardAsync(request.UserId, request.TargetId, request.Ticker, request);
return new ResponseModel
{
ResponseStatus = response.Successful(replacements: 1) ? ResponseStatus.Success : ResponseStatus.Failure
};
}
What I'm worried about is race conditions, say if two instances of the function are running at the same time, and one overwrites the entry in the db.
Is this possible? There's a very small chance of it happening, but I'm still a bit worried.
Thanks!
Yes, assuming your server has more than one thread (which will be any production capable web server), then two or more threads can be simultaneously running the same block of code. The typical way to handle this type of situation is with optimistic concurrency. What that means is that EF will attempt to save the record (optimistically assuming it will be able to without issue), and if the record ends up having been modified before it got to it, it will return an exceptions (specifically OptimisticConcurrencyException). You can see this ASP.NET getting started article for a walkthrough on how to set it up. Essentially, it just involves adding a rowversion column to your database table(s). Each time the row is updated, the value of that column mutates. Therefore, EF can check the value on the record it's trying to update with what's currently on the table. If they're the same, it can continue updating. If not, then something else modified the record and it stops the update. By catching the exception that's returned, you can then respond appropriately by reloading the data and trying to do another update.
It's highly unlikely you would end up hitting a concurrency issue multiples times, but just in case, I would recommend using something like Polly (Nuget) to handle the exception. Among other things, it allows you retry a set number of times or even forever, until no exception is raised. This then would ensure that the record would eventually get updated, even if there were multiple concurrency conflicts.
Policy
.Handle<OptimisticConcurrencyException>()
.RetryForever((exception, context) => {
// resolve concurrency issue
// See: https://msdn.microsoft.com/en-us/data/jj592904.aspx
})
.Execute(() => {
db.SaveChanges();
});

NHibernate rollback does not seem to work

My question is simple. I have the below code and user is still inserted. When I check the database just after SaveOrUpdate (and before the rollback) I see user is already inserted. It's like flush mode and transaction not working. Where am I going wrong?
using (var session = sessionFactory.OpenSession())
{
session.FlushMode = FlushMode.Never;
using (var tran = session.BeginTransaction())
{
var user = CreateUser();
session.SaveOrUpdate(user);
tran.Rollback();
}
}
If we check "just after SaveOrUpdate" ... we are still inside of one transaction. Lot of stuff could happen before final decision (commit or rollback).
And one of the operation would be to decide if object should be Created or Updated. So in case, that the ID generator is set to native/identity (e.g. SQL server) - NHibernate must execute the INSERT to get the ID. Lot of operation could be postponed, but to receive the ID - there is no way to wait.
So most likely your ID need to be obtained from DB.. and that's why INSERT happens. BUT other stuff won't be written into DB, until Flush() is called... So, I wouldn't mark described behaviour as something special.

Is there a way to do a transactionscope around an entire testfixture (for all tests)?

Basically instead of setting up data and then cleaning up in a NUnit test class. I want to do all my data inserts, run setup stored procedures etc, then rollback (cleanup) once all tests have run. Currently I am using a transactionscope in each and every test like this:
[Test]
public void TestName()
{
using (var transaction = new TransactionScope())
{
//run month end sproc
//run select sprocs
//do asserts
}
}
It is possible but I would not recommend it as test should be independent of each other, e.g., the data state should be the same before and after each test runs. Otherwise, data changed by one test could affect a subsequent test, and you cannot predict the order in which tests run
You also need to consider:
Transaction timeouts. If your tests take a long time (which may be unpredictable), the "global" transaction could easily timeout.
Transaction locking. The data changes at the start of the transaction will create locks which may affect the later selects and invalidate your tests.
You should be simulating the transaction state of "live" operation, which I assume will not include an enclosing "global" transaction which has made a bunch of data changes.
Alternatives:
Use a transaction in the test-run initialisation to set up the data, and then in the test-run clean-up use another transaction to revert the data changes. Neither transaction should span the actual tests.
Or: back up the database in the test-run initialisation, and make the data changes. Restore the database back up in the test-run clean up.
Or: mock the database (probably a lot of work!).
You can inherit your tester class from AbstractDependencyInjectionSpringContextTests (Spring.NET)
Checkout "16.3.2. Dependency Injection of test fixtures" from "http://www.springframework.net/doc-latest/reference/html/testing.html"
Also, in TestFixtureSetUp you can create the schema of your Database and on TestFixtureTearDown you can remove the schema.

transactions using C# to update a database

I'm doing a multi-layer MVVM WPF application, which is connected to an Oracle database. I need some explanations about TransactionScope. Consider the following example:
using (TransactionScope ts = new TransactionScope())
{
...
bank.setID(BankName, Branch);
check.addCheck(check);
...
ts.Complete();
}
This code is for explanation only: bank.setID() updates a record, while addCheck actually inserts a record. I couldn't figure out how to test this. I wanted to execute the update and shutdown of the database before inserting with the second method, and then check to see if the update is rolled back. Is this already right? Am I on the right track ? Is this the purpose of TransactionScope?
Thanks in advance
Edit: I wasn't sure at first if you understood DB transactions, so here's a very short description:
TransactionScope is designed to wrap a database transaction with a mechanism that is exception safe.
You use it to wrap a set of operations that should be atomic with a transaction, so if you fail on one update, all of the DB actions within that transaction get rolled back.
You use it in a using block so that if your code throws an exception, the transaction gets rolled back, instead of committed to the database.
I wanted to execute the update and shutdown the database before inserting with the second method, and then check to see if the update is rolled back ... Am I on the right track ?
Yep. Your code should already handle it:
If the DB is shut down before addCheck does an insert, then the insert should throw an exception
Since an exception gets thrown, Complete should never get called
The finally block should automatically roll back the transaction when the (hidden) finally block is reached, and Complete hasn't been called
I couldn't figure out how to test this
If you would like to write a unit test for your code, then I wouldn't suggest the "take the DB offline in the middle of execution" approach.
Instead, I'd suggest making your DB logic flexible enough to point at different DBs. Then, either remove the table or some of the columns that addCheck inserts to. Try to set up your DB so that setID succeeds, but addCheck fails.
TransactionScope is well documented. MSDN it.
to test how it works, no need to take database offline. use this snippet:
using (TransactionScope ts = new TransactionScope())
{
try
{
...
bank.setID(BankName, Branch);
throw new System.InvalidOperationException("sht happens");
check.addCheck(check);
...
ts.Complete();
}
catch
{
//catch the exception
//ts.Complete() is not called, thus update/insert rollbacks
}
}

Categories