Reputation: 12678
I'm trying to map this classes:
public interface IBusinessObject
{
Guid Id { get; set; }
}
public class Product
{
public virtual Guid Id { get; set; }
public virtual int ProductTypeId { get; set; }
}
public class ProductWeSell : Product, IBusinessObject
{
}
public class ProductWeDontSell : Product
{
}
To database with 2 tables:
[BusinessObject] COLUMNS ([Id])
[Product] COLUMNS ([Id], [ProdyctTypeId])
I want to have Type-Per-Class for BusinessObject, and Type-Per-Hierarchy with Product. This should result in this behavior:
Logically hbm mappings should be:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" name="IBusinessObject" table="BusinessObject">
<joined-subclass name="ProductWeSell" table="Product"/>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" discriminator-value="null" name="Product" table="Product">
<discriminator type="String">
<column name="ProductTypeId" not-null="false" />
</discriminator>
<subclass name="ProductWeDontSell" discriminator-value="2" />
<subclass name="ProductWeSell" discriminator-value="1" />
</class>
</hibernate-mapping>
But I get {"Duplicate class/entity mapping ProductWeSell"} error.
Upvotes: 1
Views: 1959
Reputation: 576
I'm fairly certain that with Fluent NHibernate this can not be done. I created the following TestFixture while i was trying to come up with a way. Also I'm not sure what you are trying to map. IBusinessObject seems redundant if you're going to use it for every entity in the system (CreateQuery("from object") will return every entity inheriting from object i.e. everything)
Note: Currently this fails on the IBusinessObject query (as it should as it isn't mapped). I'm not quite sure why this doesn't work without mapping the IBusinessObject interface as implicit polymorphism seems like it should cover this (See: http://nhibernate.info/doc/nh/en/index.html#inheritace-mixingpolymorphism).
[TestFixture]
public class TestFixture
{
private ISessionFactory _sessionFactory;
private ISession _session;
[SetUp]
public void Setup()
{
var fluentConfig = Fluently.Configure().Database(() => SQLiteConfiguration.Standard.InMemory().Provider<TestConnectionProvider>())
.Mappings(x=>x.FluentMappings.Add<ProductMap>()
.Add<ProductWeSellMap>()
.Add<ProductWeDontSellMap>());
var nhConfig = fluentConfig.BuildConfiguration();
_sessionFactory = fluentConfig.BuildSessionFactory();
var schema = new SchemaExport(nhConfig);
schema.Execute(false, true, false);
_session = _sessionFactory.OpenSession();
}
[Test]
public void SomeTest()
{
using (var itx = this._session.BeginTransaction())
{
var productSold = new ProductWeSell();
var productNotSold = new ProductWeDontSell();
_session.Save(productNotSold);
_session.Save(productSold);
itx.Commit();
}
using (var itx = this._session.BeginTransaction())
{
Assert.That(_session.CreateQuery("from ProductWeSell").List(), Has.Count.EqualTo(1));
Assert.That(_session.CreateQuery("from ProductWeDontSell").List(), Has.Count.EqualTo(1));
Assert.That(_session.CreateQuery("from IBusinessObject").List(), Has.Count.EqualTo(1));
Assert.That(_session.CreateQuery("from Product").List(), Has.Count.EqualTo(2));
}
}
}
public interface IBusinessObject
{
Guid Id { get; set; }
}
public class Product
{
public virtual Guid Id { get; set; }
public virtual int ProductTypeId { get; set; }
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
this.Id(x => x.Id);
this.DiscriminateSubClassesOnColumn("ProductTypeId");
}
}
public class ProductWeSell : Product, IBusinessObject
{
public const int ProductWeSellTypeId = 1;
}
public class ProductWeSellMap : SubclassMap<ProductWeSell>
{
public ProductWeSellMap()
{
this.DiscriminatorValue(ProductWeSell.ProductWeSellTypeId);
}
}
public class ProductWeDontSell : Product
{
public const int ProductWeDontSellTypeId = 2;
}
public class ProductWeDontSellMap : SubclassMap<ProductWeDontSell>
{
public ProductWeDontSellMap()
{
this.DiscriminatorValue(ProductWeDontSell.ProductWeDontSellTypeId);
}
}
This produces the following HBM mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="CellTester.Test.Database.Product, Test, Version=2.0.0.3, Culture=neutral, PublicKeyToken=a15dc1b99998d28b" table="`Product`">
<id name="Id" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Id" />
<generator class="guid.comb" />
</id>
<discriminator type="String">
<column name="ProductTypeId" />
</discriminator>
<subclass name="ProductWeSell, Test, Version=2.0.0.3, Culture=neutral, PublicKeyToken=a15dc1b99998d28b" discriminator-value="1" />
<subclass name="ProductWeDontSell, Test, Version=2.0.0.3, Culture=neutral, PublicKeyToken=a15dc1b99998d28b" discriminator-value="2" />
</class>
</hibernate-mapping>
Upvotes: 1
Reputation: 12678
I came to interesting conclusion. I decided to change my architecture and use separate classes for data access layer, and domain-UI layer, I just convert my objects when they cross the layer. Any way it is a good practice to have decoupled data-access and UI. So my data-access model now is pretty plain without any inheritance, inheritance appear only at domain-UI level.
Upvotes: 0