I have a suite of integration tests that run inside transactions.
Sometimes it seems that NHibernate transactions are not being correctly rolled back. I can't work out what causes this.
Here is a slightly simplified overview of the base class that these integration test fixtures run with:
public class IntegrationTestFixture
{
private TransactionScope _transactionScope;
private ConnectionScope _connectionScope;
[TestFixtureSetUp]
public virtual void TestFixtureSetUp()
{
var session = NHibernateSessionManager.SessionFactory.OpenSession();
CallSessionContext.Bind(session);
_connectionScope = new ConnectionScope();
_transactionScope = new TransactionScope();
}
[TestFixtureTearDown]
public virtual void TestFixtureTearDown()
{
_transactionScope.Dispose();
_connectionScope.Dispose();
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
session.Dispose();
}
}
A call to the TransactionScope's commit method is never made, therefore how is it possible that data still ends up in the database?
Update I never really got my head around the way NHibernate treats transactions but I found that calling Session.Flush() within a transaction would sometimes result in the data remaining in the database, even if the transaction is then rolled back. I am not sure why you can't call Flush, but then roll back. This is a pity because during integration testing you want to be able to hit the database and flush is the only way to do this sometimes.
I had an issue with using the Identity generator that sounds similar. In a nutshell, when saving an object using an identity generator, it has to write to the database in order to get the identity value and can do this outside a transaction.
Ayende has a blog post about this.
Related
I'm trying to get my head around WCF transactions but struggling with one concept. What is the difference in behaviour of
TransactionFlow(TransactionFlowOption.Mandatory)
and
OperationBehavior(TransactionScopeRequired = true)
As far as I can tell from the documentation they are kind of two ways of achieving the same thing, but I cannot really figure it out.
Cheers,
Stephen
When a transaction scope is required for a method, WCF will create a transaction to execute the method's logic. Transaction flows allow you to chain transactions, so that multiple service methods that require transactions can all be joined into a single transaction instead of separate transactions.
Consider the following simple setup:
[OperationBehavior(TransactionScopeRequired = true)]
public void ServiceMethod1()
{
// Executes ServiceMethod2 and ServiceMethod3
}
[OperationBehavior(TransactionScopeRequired = true)]
[TransactionFlow(TransactionFlowOption.Mandatory)]
public void ServiceMethod2()
[OperationBehavior(TransactionScopeRequired = true)]
[TransactionFlow(TransactionFlowOption.Mandatory)]
public void ServiceMethod3()
ServiceMethod1 requires a transaction, so WCF spins up a transaction and then calls ServiceMethod2 and ServiceMethod3. Although these services also require a transaction, they are flowed. So instead of spinning up new transactions, they adopt the same transaction that was created by ServiceMethod1. If anything fails along the way or the transaction isn't committed, the entire chain gets rolled back. If the transactions were not flowed, each method would spin up a transaction that acted on it's own and had no effect on other methods being called.
My ISession object's FlushMode is FlushMode.Commit.
I use the unit of work and repository pattern as defined here:
http://nhforge.org/wikis/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.aspx
I recall seeing some examples where some people call a Get() immediately followed by a Flush or a transaction commit. We're they just off their rocker, or is there a reason to do this?
From my test:
[TestMethod]
public void TestMethod1()
{
Employee e;
IRepository<Employee> empRepo;
using(UnitOfWork.Start(Enums.Databases.MyDatabase))
{
empRepo = new Repository<Employee>();
e = empRepo.GetByID(21);
}
Debug.WriteLine(e.UserName);
}
My GetByID repository function just calls Session.Get(id) and I can view the username in the output window (after the session is killed)... so whats the point of any sort of Flush or transaction commit after a Get() ? I would understand if there was a save in there somewhere.
NHibernate assumes that all database operations are done within transactions, so people use them explicitly instead of having NHibernate the RDBMS use them implicitly.
Ayende explains this in more detail in his post NH Prof Alerts: Use of implicit transactions is discouraged.
Edit: Learned something new today. It's not NHibernate using implicit transactions but the DB.
I have a system which after getting a message - enqueues it (write to a table), and another process polls the DB and dequeues it for processing. In my automatic tests I've merged the operations in the same process, but cannot (conceptually) merge the NH sessions from the two operations.
Naturally - problems arise.
I've read everything I could about getting the SQLite-InMemory-NHibernate combination to work in the testing world, but I've now ran into RANDOMLY failing tests, due to "no such table" errors. To make it clear - "random" means that the same test with the same exact configuration and code will sometimes fail.
I have the following SQLite configuration:
return SQLiteConfiguration
.Standard
.ConnectionString(x => x.Is("Data Source=:memory:; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
.Raw(NHibernate.Cfg.Environment.ReleaseConnections, "on_close");
At the beginning of my test (every test) I fetch the "static" session provider, and kindly ask it to flush the existing DB clean, and recreate the schema:
public void PurgeDatabaseOrCreateNew()
{
using (var session = GetNewSession())
using (var tx = session.BeginTransaction())
{
PurgeDatabaseOrCreateNew(session);
tx.Commit();
}
}
private void PurgeDatabaseOrCreateNew(ISession session)
{
//http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx
new SchemaExport(_Configuration)
.Execute(false, true, false, session.Connection, null);
}
So yes, it's on a different session, but the connection is pooled on SQLite, so the next session I create will see the generated schema. Yet, while most of the times it works - sometimes the later "enqueue" operation will fail because it cannot see a table for my incoming messages.
Also - that seems to happen at max one or twice per test suite run; not all the tests are failing, just the first one (and sometimes another one. Not quite sure if it's the second or not).
The worst part is the randomness, naturally. I've told myself I've fixed this several times now, just because it simply "stopped failing". At random.
This happens on FW4.0, System.Data.SQLite x86 version, Win7 64b and 2008R2 (three differen machine in total), NH2.1.2, configured with FNH, on TestDriven.NET 32b precesses and NUnit console 32b processes.
Help?
Hi I'm pretty sure I have the exact same problem as you. I open and close multiple sessions per integration test. After digging through the SQLite connection pooling and some experimenting of my own, I've come to the following conclusion:
The SQLite pooling code caches the connection using WeakReferences, which isn't the best option for caching, since the reference to the connection(s) will be cleared when there is no normal (strong) reference to the connection and the GC runs. Since you can't predict when the GC runs, this explains the "randomness". Try and add a GC.Collect(); between closing one and opening another session, your test will always fail.
My solution was to cache the connection myself between opening sessions, like this:
public class BaseIntegrationTest
{
private static ISessionFactory _sessionFactory;
private static Configuration _configuration;
private static SchemaExport _schemaExport;
// I cache the whole session because I don't want it and the
// underlying connection to get closed.
// The "Connection" property of the ISession is what we actually want.
// Using the NHibernate SQLite Driver to get the connection would probably
// work too.
private static ISession _keepConnectionAlive;
static BaseIntegrationTest()
{
_configuration = new Configuration();
_configuration.Configure();
_configuration.AddAssembly(typeof(Product).Assembly);
_sessionFactory = _configuration.BuildSessionFactory();
_schemaExport = new SchemaExport(_configuration);
_keepConnectionAlive = _sessionFactory.OpenSession();
}
[SetUp]
protected void RecreateDB()
{
_schemaExport.Execute(false, true, false, _keepConnectionAlive.Connection, null);
}
protected ISession OpenSession()
{
return _sessionFactory.OpenSession(_keepConnectionAlive.Connection);
}
}
Each of my integrationtests inherits from this class, and calls OpenSession() to get a session. RecreateDB is called by NUnit before each test because of the [SetUp] attribute.
I hope this helps you or anyone else who gets this error.
Only thing that comes into mind that you are randomly leaving session open after the test. You must make sure any existing ISession is closed before you open another one. If you are not using the using() statement or calling Dispose() manually the session might still be alive somewhere causing those random exceptions.
This question is a bit of a dupe, but I still don't understand the best way to handle flushing.
I am migrating an existing code base, which contains a lot of code like the following:
private void btnSave_Click()
{
SaveForm();
ReloadList();
}
private void SaveForm()
{
var foo = FooRepository.Get(_editingFooId);
foo.Name = txtName.Text;
FooRepository.Save(foo);
}
private void ReloadList()
{
fooRepeater.DataSource = FooRepository.LoadAll();
fooRepeater.DataBind();
}
Now that I am changing the FooRepository to Nhibernate, what should I use for the FooRepository.Save method? Should the FooRepository always flush the session when the entity is saved?
I'm not sure if I understand your question, but here is what I think:
Think in "putting objects to the session" instead of "getting and storing data". NH will store all new and changed objects in the session without any special call to it.
Consider this scenarios:
Data change:
Get data from the database with any query. The entities are now in the NH session
Change entities by just changing property values
Commit the transaction. Changes are flushed and stored to the database.
Create a new object:
Call a constructor to create a new object
Store it to the database by calling "Save". It is in the session now.
You still can change the object after Save
Commit the changes. The latest state will be stored to the database.
If you work with detached entities, you also need Update or SaveOrUpdate to put detached entities to the session.
Of course you can configure NH to behave differently. But it works best if you follow this default behaviour.
It doesn't matter whether or not you explicitly flush the session between modifying a Foo entity and loading all Foos from the repository. NHibernate is smart enough to auto-flush itself if you have made changes in the session that may affect the results of the query you are trying to run.
Ideally I try to use one session per "unit of work". This means one cohesive piece of work which may involve several smaller steps. If you feel that you do not have a seam in your architecture where you can achieve this, then managing the session inside the repository will also work. Just be aware that you are missing out on some of the power that NHibernate provides you.
I'd vote up Stefan Moser's answer if I could - I'm still getting to grips with Nh myself but I think it's nice to be able to write code like this:
private void SaveForm()
{
using (var unitofwork = UnitOfWork.Start())
{
var foo = FooRepository.Get(_editingFooId);
var bar = BarRepository.Get(_barId);
foo.Name = txtName.Text;
bar.SomeOtherProperty = txtBlah.Text;
FooRepository.Save(foo);
BarRepository.Save(bar);
UnitOfWork.CommitChanges();
}
}
so this way either the whole action succeeds or it fails and rolls back, keeping flushing/transaction management outside of the Repositories.
I'm trying to set up a integration test class that wraps each test in a transaction. This way I can rollback the transaction after each test instead of resetting the DB before each test.
I also want to be able to use transactions in the integration tests themselves.
I am using NHibernate and the Rhino Commons UnitOfWork for the the project. I am using MSTest for the tests.
I want something like this:
[TestInitialize]
public void TestInit() {
// create outer transaction scope
UnitOfWork.Start();
UnitOfWork.Current.BeginTransaction();
}
[TestCleanup]
public void TestCleanup() {
// rollback outer transaction
UnitOfWork.Current.Dispose();
}
[TestMethod]
public void IntegrationTest() {
using (UnitOfWork.Start(UnitOfWorkNestingOptions.CreateNewOrNestUnitOfWork)) {
UnitOfWork.Current.BeginTransaction();
// integration test code
UnitOfWork.Current.TransactionalFlush();
// possibly more transactions
}
}
This is the first time I have used NHibernate, Rhino Commons, and MSTest. I am not clear on the behavior of sessions with nested Rhino Commons UnitOfWorks. What I have here does not rollback the changes from the integration test.
I tried using TransactionScope from System.Transactions, but get the following error when the UnitOfWorks end:
System.InvalidOperationException: Disconnect cannot be called while a transaction is in progress..
So here are my questions:
Is there a way to get this behavior with UnitOfWork in Rhino Commons? If not, should I just reset the database before each test or is there another way to nest transactions that plays nicely with the UnitOfWork?
Thank you.
I believe UnitOfWork.Start().BeginTransaction() returns a RhinoTransaction. So to make it more clear you can try to rewrite the code as this:
using(IUnitOfWork uow = UnitOfWork.Start(UnitOfWorkNestingOptions.CreateNewOrNestUnitOfWork))
{
RhinoTransaction tx = uow.BeginTransaction();
.
.
.
.
tx.Rollback();
}
Be warned though, I have not tried the code above, let me know if it works.