Using nHibernate 3.2, C# 4.0, SQL Server 2008 R2 Express
I have two business entities - Broker and Market. They are stored in the brokers and markets tables respectively. I also have brokerMarkets table with one extra column called MinIncrement. There's a many-many relationship between Broker and Market, but only when I want to store a MinIncrement value (i.e. it's optional). My classes look like this:
public class Market : BusinessBase
{
public Market() {}
public virtual int Id { get; set; }
public virtual string Symbol { get; set; }
public virtual string Description { get; set; }
}
public class Broker : BusinessBase
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual bool IsDefault { get; set; }
public virtual bool IsActive { get; set; }
public virtual ISet<Account> Accounts { get; set; }
}
public class BrokerMarket : Market
{
public BrokerMarket() { }
public virtual Broker Broker {get; set;}
public virtual decimal MinIncrement { get; set; }
}
My mapping files look like this: Market.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Market,MooDB" table="markets">
<id name="Id" column="marketId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Symbol" column="symbol" type="String" length="10" not-null="true" />
<property name="Description" column="description" type="String" length="30" not-null="true" />
<set name="Brokers" generic="true" table="brokerMarkets">
<key column="marketId" />
<many-to-many column="brokerId" class="MooDB.BusinessLayer.Broker,MooDB" />
</set>
</class>
</hibernate-mapping>
Broker.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Broker,MooDB" table="brokers">
<id name="Id" column="brokerId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Name" column="broker" type="String" length="50" not-null="true" />
<property name="IsActive" column="isActive" type="bool" not-null="true" />
<property name="IsDefault" column="isDefault" type="bool" not-null="true" />
<set name="Markets" generic="true" table="brokerMarkets">
<key column="brokerId" />
<many-to-many column="marketId" class="MooDB.BusinessLayer.Market,MooDB" />
</set>
<set name="Accounts" table="accounts" generic="true" inverse="true">
<key column="brokerId" />
<one-to-many class="MooDB.BusinessLayer.Account,MooDB" />
</set>
</class>
</hibernate-mapping>
In my data access layer I have a method to add a BrokerMarket:
public void AddBrokerMarket(BrokerMarket bm)
{
using (ITransaction tx = _session.BeginTransaction())
{
try
{
_session.Save(bm);
_session.Flush();
tx.Commit();
}
catch (HibernateException)
{
tx.Rollback();
throw;
}
}
}
In my unit test I try and add a BrokerMarket like this:
[Test]
public void CanAddBrokerMarket()
{
Broker broker = _provider.GetBrokerById(1);
Market market = _provider.GetMarketById(2);
var brokerMarket = new BrokerMarket { Broker = broker, Description = market.Description, Symbol = market.Symbol, MinIncrement = 0.01M };
_provider.AddBrokerMarket(brokerMarket);
}
When I run the test I get this error:
Running the tests. Test 'MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket' failed: Message NHibernate.MappingException : No persister for: MooDB.BusinessLayer.BrokerMarket Stack Trace at NHibernate.Impl.SessionFactoryImpl.GetEntityPersister(String entityName) at NHibernate.Impl.SessionImpl.GetEntityPersister(String entityName, Object obj) at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess) at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) at NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event) at NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event) at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event) at NHibernate.Impl.SessionImpl.Save(Object obj) NHibernateDataProvider.cs(91,0): at MooDB.DataLayer.NHibernateDataProvider.AddBrokerMarket(BrokerMarket bm) DataLayerTests.cs(81,0): at MooDB.Tests.DataLayerTests.CanAddBrokerMarket()
I'm not sure what I've done wrong. Do I need to add a Broker collection in the Market class and a Market collection in the Broker class? I did try that but I get the same error, and I think I'm missing something conceptually somewhere.
UPDATE
I have implemented the following changes: Market.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Market,MooDB" table="markets">
<id name="Id" column="marketId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Symbol" column="symbol" type="String" length="10" not-null="true" />
<property name="Description" column="description" type="String" length="30" not-null="true" />
<map name="BrokerMarkets" table="brokerMarkets" lazy="true" cascade="none">
<cache usage="read-write"/>
<key column="marketId" />
<index column="brokerId" type="Int32" />
<composite-element class="MooDB.BusinessLayer.BrokerMarket,MooDB">
<parent name="Market"/>
<property name="MinIncrement" column="minIncrement" type="decimal" />
<many-to-one name="Broker" class="MooDB.BusinessLayer.Broker,MooDB" column="brokerId" />
</composite-element>
</map>
</class>
</hibernate-mapping>
Broker.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Broker,MooDB" table="brokers">
<id name="Id" column="brokerId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Name" column="broker" type="String" length="50" not-null="true" />
<property name="IsActive" column="isActive" type="bool" not-null="true" />
<property name="IsDefault" column="isDefault" type="bool" not-null="true" />
<set name="Accounts" table="accounts" generic="true" inverse="true">
<key column="brokerId" />
<one-to-many class="MooDB.BusinessLayer.Account,MooDB" />
</set>
</class>
</hibernate-mapping>
Market class:
public class Market : BusinessBase
{
public Market() {}
public virtual int Id { get; set; }
public virtual string Symbol { get; set; }
public virtual string Description { get; set; }
public virtual ISet<BrokerMarket> BrokerMarkets { get; set; } //added this line
}
BrokerMarket class
public class BrokerMarket : Market
{
public BrokerMarket() { }
public virtual Broker Broker {get; set;}
public virtual Market Market { get; set; } // added this line
public virtual decimal MinIncrement { get; set; }
}
Everything else is unchanged. I run the unit test again and I get:
Running the tests. Test 'MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket' failed: Message TestFixtureSetUp failed in DataLayerTests
Test 'MooDBTests/MooDB/Tests/DataLayerTests' failed: Message SetUp : NHibernate.MappingException : Repeated column in mapping for collection: MooDB.BusinessLayer.Market.BrokerMarkets column: brokerId
UPDATE 2
If I remove
<index column="brokerId" type="Int32" />
from Market.hbm.xml I get this error when running the unit test:
Running the tests. Test 'MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket' failed: Message TestFixtureSetUp failed in DataLayerTests
Test 'MooDBTests/MooDB/Tests/DataLayerTests' failed: Message SetUp : NHibernate.MappingException : MooDB.Mappings.Market.hbm.xml(17,10): XML validation error: The element 'map' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'composite-element' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'map-key, composite-map-key, map-key-many-to-many, index, composite-index, index-many-to-many, index-many-to-any' in namespace 'urn:nhibernate-mapping-2.2'. ----> System.Xml.Schema.XmlSchemaValidationException : The element 'map' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'composite-element' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'map-key, composite-map-key, map-key-many-to-many, index, composite-index, index-many-to-many, index-many-to-any' in namespace 'urn:nhibernate-mapping-2.2'.