Okay so here is the problem I am having. I have several unit tests for a repository, I get a green light on all of them individually. Yet when I run all the tests together, the first one succeeds and then I get a foreign key constraint error for the others, this is only happening where I have a many-to-one relationship. I have tried playing with the cascade option and I removed the first test to see if it was corrupting the test data but all that happened was I got a green light on the first test and the rest went red. Again in isolation I got a green light on all tests.
Most of this code is based upon the “Your first NHibernate based application” tut on NHForge.com
The error is occuring on the StockTransactionRepository. I have tried to include the relavent code, if you need more let me know. I suspect there is something glaringly obvious here.
The error:
NHibernate.Exceptions.GenericADOException : could not insert: [rapidstk_base.Domain.StockTransaction#f305a5d4-9-c6e-bcdf-9d4dd16337ff][SQL: INSERT INTO StockTransaction (stkitm_StockItemToTransact, dcm_StockTransactionAmount, stkloc_StockLocation, dt_StockTransactionDate, Id) VALUES (?,?,?,?,?)]
---->MySql.Data.MySqlClient.MySqlException: Cannot add or update a child row: a foreign key constraint fails (`rpdstkdb`.`stocktransaction`, CONSTRAINT `FK37E1C2103E2BF85C` FOREIGN KEY (`stkitm_StockItemToTransact`) REFERENCES `stockitem` (`ID`))
hbm.xml code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="rapidstk_base"
namespace="rapidstk_base.Domain"
auto-import="false">
<class name="rapidstk_base.Domain.StockTransaction">
<id name="Id">
<generator class="guid" />
</id>
<many-to-one name="stkitm_StockItemToTransact" class="StockItem" cascade="all" />
<property name="dcm_StockTransactionAmount" />
<many-to-one name="stkloc_StockLocation" class="StockLocation" cascade="all" />
<property name="dt_StockTransactionDate" />
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="rapidstk_base"
namespace="rapidstk_base.Domain"
auto-import="false">
<class name="rapidstk_base.Domain.StockItem">
<id name="Id">
<generator class="guid" />
</id>
<property name="str_StockItemName" />
<property name="str_StockItemDescription" />
<property name="dtm_StockItemCreationDate" />
</class>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="rapidstk_base"
namespace="rapidstk_base.Domain"
auto-import="false">
<class name="rapidstk_base.Domain.StockLocation">
<id name="Id">
<generator class="guid" />
</id>
<property name="s_StockLocationName" />
<property name="s_StockLocationDescription" />
</class>
</hibernate-mapping>
Unit Tests:
using NUnit.Framework;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using NHibernate;
using System;
using System.Collections.Generic;
using rapidstk_base.Domain;
using rapidstk_base.Repositories;
using rapidstk_base;
namespace rapidstk_base_test
{
[TestFixture]
public class StockTransactionRepositoryTest
{
private ISessionFactory _sessionFactory;
private Configuration _configuration;
private readonly static StockItem[] _stockitems = new[]
{
new StockItem { str_StockItemName = "Orgone Accumulator", str_StockItemDescription = "Social Integrator." },
new StockItem { str_StockItemName = "Perpetual Dingle", str_StockItemDescription = "Everlasting Bliss." },
new StockItem { str_StockItemName = "Apple", str_StockItemDescription = "Golden Delicious." },
new StockItem { str_StockItemName = "Nepenthe", str_StockItemDescription = "The answer." },
new StockItem { str_StockItemName = "Bobbins Gourd", str_StockItemDescription = "Follow The Gourd." },
};
private readonly static StockLocation[] _stocklocations = new[]
{
new StockLocation() { s_StockLocationName="Bay 1", s_StockLocationDescription="Upstairs next to the coffee machine."},
new StockLocation() { s_StockLocationName="Bay 2", s_StockLocationDescription="Under the Shrubbery."},
new StockLocation() { s_StockLocationName="Bay Watch", s_StockLocationDescription="Bouncing on the Beach."},
new StockLocation() { s_StockLocationName="My Pocket", s_StockLocationDescription="Lintville."},
new StockLocation() { s_StockLocationName="Secret Lair", s_StockLocationDescription="Next to the coke machine."},
};
private readonly StockTransaction[] _stockTransactions = new[]
{
new StockTransaction { stkitm_StockItemToTransact = _stockitems[0], stkloc_StockLocation = _stocklocations[0], dcm_StockTransactionAmount = 10.0M },
new StockTransaction { stkitm_StockItemToTransact = _stockitems[1], stkloc_StockLocation = _stocklocations[1], dcm_StockTransactionAmount = -10.0M },
new StockTransaction { stkitm_StockItemToTransact = _stockitems[2], stkloc_StockLocation = _stocklocations[2], dcm_StockTransactionAmount = 1.0M },
new StockTransaction { stkitm_StockItemToTransact = _stockitems[3], stkloc_StockLocation = _stocklocations[3], dcm_StockTransactionAmount = 2.9M },
new StockTransaction { stkitm_StockItemToTransact = _stockitems[4], stkloc_StockLocation = _stocklocations[4], dcm_StockTransactionAmount = 155.0M, dt_StockTransactionDate=DateTime.Parse("2011/11/30") },
new StockTransaction { stkitm_StockItemToTransact = _stockitems[4], stkloc_StockLocation = _stocklocations[4], dcm_StockTransactionAmount = -50.0M, dt_StockTransactionDate=DateTime.Parse("2011/12/01") },
new StockTransaction { stkitm_StockItemToTransact = _stockitems[4], stkloc_StockLocation = _stocklocations[4], dcm_StockTransactionAmount = -50.0M, dt_StockTransactionDate=DateTime.Parse("2011/12/02") },
new StockTransaction { stkitm_StockItemToTransact = _stockitems[4], stkloc_StockLocation = _stocklocations[4], dcm_StockTransactionAmount = -50.0M, dt_StockTransactionDate=DateTime.Parse("2011/12/03") },
};
private void CreateInitialData()
{
using (ISession session = _sessionFactory.OpenSession())
{
using(ITransaction transaction = session.BeginTransaction())
{
foreach(var tr in _stockTransactions)
{
session.Save(tr);
}
transaction.Commit();
}
}
}
[TestFixtureSetUp]
public void TestFixtureSetup()
{
_configuration = new Configuration();
_configuration.Configure ();
_configuration.AddAssembly(typeof(rapidstk_base.Domain.StockTransaction).Assembly);
_sessionFactory = _configuration.BuildSessionFactory();
}
[SetUp]
public void SetupContext()
{
var schema = new SchemaExport(_configuration);
schema.Create(true, true);
CreateInitialData();
}
[Test]
public void CanAddTransaction ()
{
var newStkTransaction = new StockTransaction{ stkitm_StockItemToTransact=_stockitems[0], stkloc_StockLocation=_stocklocations[3], dcm_StockTransactionAmount=42.0M };
IStockTransactionRepository repository = new StockTransactionRepository();
repository.Add(newStkTransaction);
using(ISession session = _sessionFactory.OpenSession())
{
var fromdb = session.Get<StockTransaction>(newStkTransaction.Id);
Assert.IsNotNull(fromdb);
Assert.AreNotSame(newStkTransaction, fromdb);
Assert.AreEqual(newStkTransaction.stkitm_StockItemToTransact.str_StockItemName, fromdb.stkitm_StockItemToTransact.str_StockItemName);
Assert.AreEqual(newStkTransaction.stkitm_StockItemToTransact.str_StockItemDescription, fromdb.stkitm_StockItemToTransact.str_StockItemDescription);
Assert.AreEqual(newStkTransaction.stkloc_StockLocation.s_StockLocationName, fromdb.stkloc_StockLocation.s_StockLocationName);
Assert.AreEqual(newStkTransaction.stkloc_StockLocation.s_StockLocationDescription, fromdb.stkloc_StockLocation.s_StockLocationDescription);
Assert.AreEqual(newStkTransaction.dcm_StockTransactionAmount, fromdb.dcm_StockTransactionAmount);
}
}
[Test]
public void CanRemoveTransaction()
{
IStockTransactionRepository repository = new StockTransactionRepository();
Guid id = _stockTransactions[0].Id;
repository.Remove(_stockTransactions[0]);
using(ISession session = _sessionFactory.OpenSession())
{
var fromdb = session.Get<StockLocation>(id);
Assert.IsNull(fromdb);
}
}
[Test]
public void CanUpdateTransaction()
{
IStockTransactionRepository repository = new StockTransactionRepository();
var stkTransaction = _stockTransactions[1];
stkTransaction.dcm_StockTransactionAmount = 150.0M;
repository.Update(stkTransaction);
using(ISession session = _sessionFactory.OpenSession())
{
var fromdb = session.Get<StockTransaction>(stkTransaction.Id);
Assert.AreEqual(stkTransaction.dcm_StockTransactionAmount, fromdb.dcm_StockTransactionAmount);
}
}
[Test]
public void CanGetAllTransactionForAProduct()
{
IStockTransactionRepository repository = new StockTransactionRepository();
StockItem queryItem = _stockitems[4];
var fromdb = repository.GetByStockItem(queryItem);
Assert.AreEqual(4, fromdb.Count);
Assert.IsTrue(IsInCollection(_stockTransactions[5], fromdb));
Assert.IsTrue(IsInCollection(_stockTransactions[6], fromdb));
}
private bool IsInCollection(StockTransaction stkTrns, ICollection<StockTransaction> fromdb)
{
foreach(var item in fromdb)
if(stkTrns.Id == item.Id)
return true;
return false;
}
[Test]
public void CanGetAllTransactionsUpToADate()
{
IStockTransactionRepository repository = new StockTransactionRepository();
StockItem queryItem = _stockitems[4];
var fromdb = repository.GetByStockItem(queryItem, DateTime.Parse ("2011/12/02"));
Assert.AreEqual(3, fromdb.Count);
Assert.IsTrue(IsInCollection(_stockTransactions[5], fromdb));
Assert.IsTrue(IsInCollection(_stockTransactions[6], fromdb));
Assert.IsFalse(IsInCollection(_stockTransactions[7], fromdb));
}
[Test]
public void CanGetAllTransactionsBetweenDates()
{
IStockTransactionRepository repository = new StockTransactionRepository();
StockItem queryItem = _stockitems[4];
var fromdb = repository.GetByStockItem(queryItem, DateTime.Parse ("2011/12/01"), DateTime.Parse ("2011/12/03"));
Assert.AreEqual(3, fromdb.Count);
Assert.IsFalse(IsInCollection(_stockTransactions[4], fromdb));
Assert.IsTrue(IsInCollection(_stockTransactions[5], fromdb));
Assert.IsTrue(IsInCollection(_stockTransactions[6], fromdb));
Assert.IsTrue(IsInCollection(_stockTransactions[7], fromdb));
}
}
}
Stock Transaction Repository Code:
using System;
using System.Collections.Generic;
using rapidstk_base.Domain;
using NHibernate;
using NHibernate.Criterion;
namespace rapidstk_base.Repositories
{
public class StockTransactionRepository : IStockTransactionRepository
{
public void Add(StockTransaction stkTransaction)
{
using(ISession session=NHibernateHelper.OpenSession())
using(ITransaction transaction = session.BeginTransaction())
{
session.Save(stkTransaction);
transaction.Commit();
}
}
public void Remove(StockTransaction stkTransaction)
{
using(ISession session=NHibernateHelper.OpenSession())
using(ITransaction transaction = session.BeginTransaction())
{
session.Delete(stkTransaction);
transaction.Commit();
}
}
public void Update(StockTransaction stkTransaction)
{
using(ISession session = NHibernateHelper.OpenSession())
using(ITransaction transaction = session.BeginTransaction())
{
session.Update(stkTransaction);
transaction.Commit();
}
}
public ICollection<StockTransaction> GetByStockItem(StockItem item)
{
using(ISession session = NHibernateHelper.OpenSession())
{
var transactions = session.CreateCriteria(typeof(StockTransaction))
.Add(Restrictions.Eq("stkitm_StockItemToTransact", item))
.List<StockTransaction>();
return transactions;
}
}
public ICollection<StockTransaction> GetByStockItem(StockItem item, DateTime endDate)
{
using(ISession session = NHibernateHelper.OpenSession())
{
var transactions = session.CreateCriteria(typeof(StockTransaction))
.Add(Restrictions.Eq("stkitm_StockItemToTransact", item))
.Add(Restrictions.Le("dt_StockTransactionDate", endDate))
.List<StockTransaction>();
return transactions;
}
}
public ICollection<StockTransaction> GetByStockItem(StockItem item, DateTime startDate, DateTime endDate)
{
using(ISession session = NHibernateHelper.OpenSession())
{
var transactions = session.CreateCriteria(typeof(StockTransaction))
.Add(Restrictions.Eq("stkitm_StockItemToTransact", item))
.Add(Restrictions.Le("dt_StockTransactionDate", endDate))
.Add(Restrictions.Ge("dt_StockTransactionDate", startDate))
.List<StockTransaction>();
return transactions;
}
}
public StockTransactionRepository ()
{
}
}
}
I think what is happening is because your StockItems, StockLocations, and Stocktransactions are static, NHibernate associates them with the session on the first test and replaces any associates with proxies. Then on subsequent tests, they are not persisted properly and you’re getting this FK constraint error. Try making them non static and initialize them just before your
CreateInitialDatacall. So your Seetup might look something like: