Fluent NHibernate trying to map subclass of subclass using table-per-subclass - nhibernate

I am building an application with heavy inheritance and have a part where there exists classes, A, B and C with:
class A
class B : A
class C : B
I implemented subclass mapping as a table-per-subclass style for class B as follows:
class BMap : SubclassMap<B>
{
public BMap()
{
Extends<A>();
KeyColumn("ID");
}
}
Which works perfectly. However, when I want to implement C as follows:
class CMap : SubclassMap<C>
{
public CMap()
{
Extends<B>();
KeyColumn("ID");
}
}
It results in the error
Duplicate class/entity mapping
I browsed the Hibernate/NHibernate forum but could not find an answer to this problem.

this does work as expected with NH 3.3.1.4000
public class A
{
public virtual int Id { get; protected set; }
}
public class B : A { }
public class C : B { }
public class AMap : ClassMap<A>
{
public AMap()
{
Id(x => x.Id);
}
}
public class BMap : SubclassMap<B> { }
public class CMap : SubclassMap<C> { }
public static void Main(string[] args)
{
var config = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql().FormatSql())
.Mappings(m => m.FluentMappings
.Add<AMap>()
.Add<BMap>()
.Add<CMap>()
)
.BuildConfiguration();
using (var sf = config.BuildSessionFactory())
using (var session = sf.OpenSession())
{
new SchemaExport(config).Execute(true, true, false, session.Connection, null);
session.Save(new C());
session.Flush();
}
}

Related

Is there #MappingSupperclass equivalent in NHibernate

I recently started the Asp Net Core project with NHibernate as an orm. I have definitely little experience with NHibernate.
I am looking for an equivalent for the #MappedSuperclass Hibernate annotation. Either I can't find a good solution or I can't use any found.
But from the beginning:
Each of my entities inherits from the base class BaseEntity, containing several technical fields / columns
public class BaseEntity
{
public virtual long id { get; set; }
public virtual DateTime inDate { get; set; }
public virtual DateTime? outDate { get; set; }
//some other properies
}
public class AnyClass : BaseEntity
{
//some AnyClass properties
}
public class AnyClassMap : ClassMapping<AnyClass>
{
public AnyClassMap()
{
Table("any_table");
//Copy/Paste to each mapping class
Id(b => b.id, x => { x.Column("id"); x.Generator(Generators.Native); x.Type(NHibernateUtil.Int64); });
Property(b => b.inDate, x => { x.Column("in_date"); });
Version(b => b.outDate, x => { x.Column("out_date"); x.Type(NHibernateUtil.DbTimestamp); });
//Some other BaseEntity properties
//some AnyClass properties
}//ctor
}
In every mapping class I repeat mappings about the properties of the base class. However, I would like to achieve something similar to:
public class BaseEntityMap : MappedSuperclassEquvalent<BaseEntity>
{
public BaseEntityMap()
{
Id(b => b.id, x => { x.Column("id"); x.Generator(Generators.Native); x.Type(NHibernateUtil.Int64); });
Property(b => b.inDate, x => { x.Column("in_date"); });
Version(b => b.outDate, x => { x.Column("out_date"); x.Type(NHibernateUtil.DbTimestamp); });
//Some other BaseEntity properties
}//ctor
}
public class AnyClassMap : ClassMapping<AnyClass>
{
public AnyClassMap()
{
Table("any_table");
//example only
IncludeMapping(BaseEntityMap.Class);
//some AnyClass properties
}//ctor
}
I know that I can use BaseEntity as ComponentMapping and then use it in the AnyClass class as property.
However, ComponentMapping does not allow id and version mapping. On the other hand, for many reasons I care about inheritance from the base entity.
Thank you in advance
Just create base mapping class based on ClassMapping with all stuff you need:
public class BaseEntityClassMapping<TBaseEntity>: ClassMapping<TBaseEntity> where TBaseEntity: BaseEntity
{
public BaseEntityClassMapping()
{
Id(b => b.id, x => { x.Column("id"); x.Generator(Generators.Native); x.Type(NHibernateUtil.Int64); });
Property(b => b.inDate, x => { x.Column("in_date"); });
Version(b => b.outDate, x => { x.Column("out_date"); x.Type(NHibernateUtil.DbTimestamp); });
//Some other BaseEntity properties
}
}
And make your mappings based on this class:
public class AnyClassMap : BaseEntityClassMapping<AnyClass>
{
public AnyClassMap()
{
Table("any_table");
//some AnyClass properties
}
}

Different sequences for subclasses with Fluent NHibernate

I am using Fluent NHibernate and table per concrete class for inheritance mappings.
There is an abstract base class and two other subclasses.
My base class has Id column. Created tables are ok. All tables has its own Id column. But sequence is only one for these two tables.
I want to assign different sequence for every subclass.
public abstract class Base
{
public virtual int Id { get; set; }
}
public class BaseMap : ClassMap<Base>
{
public BaseMap()
{
Id(x => x.Id).GeneratedBy.Native();
}
}
public class A : Base
{
public virtual int AmountOfA { get; set; }
}
public class AMap : ClassMap<A>
{
public AMap()
{
Map(x => x.AmountOfA );
}
}
public class B : Base
{
public virtual int AmountOfB { get; set; }
}
public class BMap : ClassMap<B>
{
public BMap()
{
Map(x => x.AmountOfB );
}
}
Is this possible with Fluent NHibernate?
this is by design.
session.Get<Base>(1); would have undefined behavior because the id is not unique
consider the case when there is a reference to the base class Reference(x => x.SomeBase); with a the value 1 in the database: it would not be possible to know if A(Id: 1) or B(Id: 1) is the object referenced
if Base is only there to reuse the Id property then dont map it as own entity but create a base class
public class BaseMap<T> : ClassMap<T> where T : Base
{
public BaseMap()
{
Id(x => x.Id).GeneratedBy.Native();
}
}
public class AMap : BaseMap<A>
{
public AMap()
{
Map(x => x.AmountOfA );
}
}

Fluent Nhibernate Enum Mapping

I have some problem with enum mapping in fluent NHibernate. I know this question has been asked many times but I couldn't find any solution that worked for me. I'm newbie in NHibernate and it looks like I may have missed something simple and stupid. Here is my code.
public class DBPublication
{
public virtual int pub_id { get; set; }
public virtual PublicationStatuses status { get; set; }
...
}
public enum PublicationStatuses
{
on_moderation,
active,
...
}
public class DBPublicationMap : ClassMap<DBPublication>
{
public DBPublicationMap()
{
Table("content.publications");
Id(x => x.pub_id).GeneratedBy.Sequence("content.pub_sq");
Map(x => x.status);
...
}
}
postgres enum type
CREATE TYPE content.enum_publication_status AS ENUM('on_moderation', 'active', ...);
but when I try to save, postgres throws this
column "status" is of type content.enum_publication_status but expression is of type text
any suggestion?
Here is a working sample of configuring nhibernate to store enum field.
public class Entity
{
public virtual int id { get; set; }
public virtual SomeEnum EnumField { get; set; }
}
public enum SomeEnum
{
Value1,
Value2
}
class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Id(x => x.id).GeneratedBy.Native();
Map(x => x.EnumField);
}
}
class Program
{
static void Main(string[] args)
{
var factory = Fluently.Configure().Mappings(x => x.FluentMappings.AddFromAssemblyOf<Entity>())
.ExposeConfiguration(config => new SchemaExport(config).Create(false, true))
.Database(MsSqlConfiguration.MsSql2008.ConnectionString("Data Source=.;Initial Catalog=nhtest;Integrated Security=True"))
.BuildSessionFactory();
using (var session = factory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var entity = new Entity();
entity.EnumField = SomeEnum.Value2;
session.Save(entity);
transaction.Commit();
}
}
}
}
In such case it is stored as strings in data base. If you want it be saved as integers, you need to change mapping for Enum field property to the following:
Map(x => x.EnumField).CustomType<int>();
Why are you creating enum types in progres? isn't this a maintenance-nightmare?
What does your configuration look like? Have you already tried using the conventions discribed here?
Looks like a simple casting error, so please consider using CustomType() / CustomSQLType() inside your mapping.
Simply add this class to your project:
public class PgEnumMapper<T> : NHibernate.Type.EnumStringType<T>
{
public override NHibernate.SqlTypes.SqlType SqlType
{
get { return new NHibernate.SqlTypes.SqlType(System.Data.DbType.Object); }
}
}
Then, you can use:
Map(x => x.status).CustomType<PgEnumMapper<PublicationStatuses>>();

Using discriminator with Fluent NHibernate

I'm trying to create a discriminator column. This column would hold one of the many statuses available. Like my code will show, each status has a name as well as a background color. Each status shares the same base class.
Here is my code:
public class Item
{
public virtual int Id { get; set; }
public virtual Status ItemStatus { get; set; }
}
public abstract class Status
{
private readonly int _id;
public static readonly Status Foo = new FooStatus(1);
public static readonly Status Bar = new BarStatus(2);
public Status()
{
}
protected Status(int id)
{
_id = id;
}
public virtual int Id { get { return _id; } }
public abstract string Name { get; }
public abstract string BackgroundColor { get; }
}
public class FooStatus : Status
{
public FooStatus()
{
}
public FooStatus(int id)
: base(id)
{
}
public override string Name
{
get { return "Foo Status"; }
}
public override string BackgroundColor
{
get { return "White"; }
}
}
public class BarStatus : Status
{
public BarStatus()
{
}
public BarStatus(int id)
: base(id)
{
}
public override string Name
{
get { return "Bar Status"; }
}
public override string BackgroundColor
{
get { return "Black"; }
}
}
And here is my mapping:
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(x => x.Id).GeneratedBy.Identity();
DiscriminateSubClassesOnColumn<int>("ItemStatus", 0).AlwaysSelectWithValue();
}
}
Essentially, what I'd like is that if I set ItemStatus to Status.Foo then the ItemStatus column would have a value of 1. What I have now doesn't throw any exceptions, but it always inserts ItemStatus as 0.
This is the inserting code I'm using:
using (var session = sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var item = new Item
{
ItemStatus = Status.Foo
};
session.Save(item);
transaction.Commit();
var firstItem = session.Get<Item>(1);
Console.WriteLine(firstItem.ItemStatus.Name);
}
Where can I read up on this topic using FNH?
Before anyone suggests be to check on Google I did search several things but nowhere can I find a full example.
Your SubclassMap would look something like this:
public class FooStatusMap : SubclassMap<FooStatus>
{
public FooStatusMap()
{
DiscriminatorValue(1);
}
}
This is called "table-per-class-hierarchy," and you're right it doesn't look like there are many resources on it out there.
I believe if you don't call DiscriminatorValue in a SubclassMap, NHibernate attempts to discriminate by looking at the name of the subclass being mapped and seeing if it matches up with the value in the discriminator column.
I wouldnt write submaps for all the subclasses you can just do this instead
public class FooMap: ClassMap<T>
{
//other mapping
DiscriminateSubClassesOnColumn("DiscriminatorColumn")
.SubClass<Foo1>(m => { })
.SubClass<Foo2>(m => { })
.SubClass<Foo3>(m => { });
}
Hope that helps
If you're open to the Discriminator column having the class names of the derived classes, you can implement this via automapping.
In your session factory:
private static ISessionFactory CreateSessionFactory()
{
var cfg = new MyMappingConfiguration();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("MyConnectionKey")).FormatSql().ShowSql()
)
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Status>(cfg)
.IncludeBase<Status>()
.Conventions.Add<PrimaryKeyConvention>()))
.BuildSessionFactory();
}
Then add the MyMappingConfiguration override:
public class MappingConfiguration : DefaultAutomappingConfiguration
{
public override bool IsId(Member member)
{
return member.Name == member.DeclaringType.Name + "Id";
}
public override bool IsDiscriminated(Type type)
{
return true;
}
}
Hope that h

NHibernate: Cannot assign property value in entity's constructor

When I run the following code, at B's constructor, I got a NullReferenceException. I cannot assign value to B's Number property.
Models:
public class A
{
public A()
{
this.Number = 1;
if (this.Number != 1)
{
throw new Exception("Failed to assign value in A's constructor");
}
}
public virtual int Id { get; private set; }
public virtual int Number { get; set; }
public virtual B B { get; set; }
}
public class B
{
public B()
{
this.Number = 1;
// Throws NullReferenceException: Object reference not set to an instance of an object.
if (this.Number != 1)
{
throw new Exception("Failed to assign value in B's constructor");
}
}
public virtual int Id { get; private set; }
public virtual int Number { get; set; }
}
Mappings:
public class AMappings : ClassMap<A>
{
public AMappings()
{
Id(x => x.Id);
Map(x => x.Number);
References(x => x.B).Cascade.All();
}
}
public class BMappings : ClassMap<B>
{
public BMappings()
{
Id(x => x.Id);
Map(x => x.Number);
}
}
Main method:
class Program
{
static void Main(string[] args)
{
// Create connection string
string connectionString = new System.Data.SqlClient.SqlConnectionStringBuilder()
{
DataSource = #".\r2",
InitialCatalog = "TestNHibernateMappings",
IntegratedSecurity = true
}.ConnectionString;
// Create SessionFactory
ISessionFactory sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration
.MsSql2008.ConnectionString(connectionString)
.ShowSql())
.Mappings(m => m.FluentMappings
.Add(typeof(AMappings))
.Add(typeof(BMappings)))
.ExposeConfiguration(BuildSchema)
.BuildConfiguration()
.BuildSessionFactory();
// Create test object in DB
using (var session = sessionFactory.OpenSession())
{
using (var trans = session.BeginTransaction())
{
var a = new A();
a.B = new B();
session.Save(a);
trans.Commit();
}
}
// Read test object from DB
using (var session = sessionFactory.OpenSession())
{
using (var trans = session.BeginTransaction())
{
var a = session.Get<A>(1);
}
}
}
static void BuildSchema(Configuration cfg)
{
new SchemaExport(cfg).Create(false, true);
}
}
I'm using NHibernate 3.1.0.4000, FluentNHibernate 1.2.0.712.
Any ideas? Thanks.
The key here is that Number is virtual.
A.B is being lazy-loaded. NHibernate creates a proxy for B which overrides each virtual property in the class. The first time one of the non-Id properties is accessed, NHibernate will load the data from the database to populate the object.
Since this proxy class is a subclass of B, B's constructor will be called before the proxy constructor. When B's constructor sets the virtual property Number, it is calling the Number property as defined in the proxy subclass, which has not yet been initialized.
For a more thorough discussion of constructors and inheritance, see http://www.yoda.arachsys.com/csharp/constructors.html
To fix this, convert any properties you wish to set in the constructor to use backing fields instead of auto-properties, then set the field instead of the property in the constructor.
public class B
{
public B()
{
_number = 1;
}
public virtual int Id { get; private set; }
private int _number;
public virtual int Number
{
get { return _number; }
set { _number = value; }
}
}
It's a bit more verbose, but it effectively avoids touching virtual methods or properties in the constructor.