Fluent Nhibernate How to specify Id() in SubclassMap - nhibernate

I'm in the process of adapting Fluent NHibernate to our existing legacy app and am trying to determine how to use ClassMap and SubclassMap for the entity hierarchy shown.
// BaseObject contains database columns common to every table
public class BaseObject
{
// does NOT contain database id column
public string CommonDbCol1 { get; set; }
public string CommonDbCol2 { get; set; }
// ...
}
public class Entity1 : BaseObject
{
public int Entity1Id { get; set; }
// other Entity1 properties
}
public class Entity2 : BaseObject
{
public int Entity2Id { get; set; }
// other Entity2 properties
}
The identity columns for Entity1 and Entity2 are uniquely named per table. BaseObject contains columns that are common to all of our entities. I am not using AutoMapping, and thought I could use ClassMap on the BaseObject, and then use SubclassMap on each Entity like this:
public class Entity1Map : SubclassMap<Entity1>
{
public Entity1Map()
{
Id(x => x.Entity1Id);
// ...
}
}
Problem is, Id() is not defined for SubclassMap. So, how do I specify within each Entity1Map, Entity2Map, ... (we have 100+ entity classes all inheriting from BaseObject) what the entity-specific Id is?
Thanks in advance for any insight!

It's not possible to do that in either Fluent NHibernate or NHibernate. Do you actualy want your classes to be mapped as subclasses, or do you just want them to share the common mappings? If you truly want subclasses, then you're going to need to have them share the identity column, no other way around it; if you don't want actual subclasses, create an abstract ClassMap<T> where T : BaseObject and map the common properties in there.
Something like:
public abstract class BaseObjectMap<T> : ClassMap<T> where T : BaseObject
{
public BaseObjectMap()
{
Map(x => x.CommonProperty1);
}
}

Related

Map two classes to the same table

There is a NODES table with dozen of 'small' columns and a LOB column in a legacy DB. A NodeEntity class is mapped to the NODES table.
For performance purposes I do not want to load LOB column every time I access the DB. I know two approaches to achieve this:
Lazy loaded properties
Separate entity class (the idea is taken from here)
Lazy loaded properties are good when you only loading data from DB. But if you have to save entities then there is a risk to lose your data if you forget to fetch lazy loaded properties beforehand.
So I chose the second approach.
I created separate small NodeEntityLite class with properties mapped to non-LOB columns of NODES table. I modified NodeEntity class so it inherits from NodeEntityLite class. I changed the mappings for my classes and used union-subclass for inheritance.
public class NodeEntityLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
}
public class NodeEntity : NodeEntityLite {
public virtual string NOTE { get; set; } // type:clob
}
FluentNHibernate mapping for NodeEntityLite class is
public void Override(AutoMapping<NodeEntityLite> mapping) {
mapping.Table("NODES");
mapping.UseUnionSubclassForInheritanceMapping();
}
FluentNHibernate mapping for NodeEntity class is
public void Override(AutoMapping<NodeEntity> mapping) {
mapping.Table("NODES");
mapping.Map(e => e.NOTE).CustomType("StringClob").CustomSqlType("NCLOB");
}
I expected that when I execute select n from NodeEntityLite n where n.Id = :p0 HQL then NHibernate generates SQL commands without NOTE column:
select nodeentity0_.ID as id1_87_,
nodeentity0_.CODE as code2_87_
from from NODES nodeentity0_
where nodeentity0_.ID=:p0;
But NHibernate generates absolutely different SQL command (NOTE column is not skipped as I expected):
select nodeentity0_.ID as id1_87_,
nodeentity0_.CODE as code2_87_,
nodeentity0_.NOTE as note14_87_,
nodeentity0_.clazz_ as clazz_
from ( select ID, CODE, NOTE, 1 as clazz_ from NODES ) nodeentity0_
where nodeentity0_.ID=:p0;
I tried to change inheritance and to use other mappings but without success.
The question is: Can I map several classes to the same table in NHibernate to get access to different columns?
If yes, please give an example.
The solution (based on the suggestions from David Osborne and mxmissile) is not to use inheritance. I use common interface implementation instead of class inheritance. The working code is below:
public interface INodeLite {
long Id { get; set; }
string Code { get; set; }
}
public class NodeEntityLite : INodeLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
}
public class NodeEntity : INodeLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
public virtual string NOTE { get; set; } // type:clob
}
...
public void Override(AutoMapping<NodeEntityLite> mapping) {
mapping.Table("NODES");
}
...
public void Override(AutoMapping<NodeEntity> mapping) {
mapping.Table("NODES");
mapping.Map(e => e.NOTE).CustomType("StringClob").CustomSqlType("NCLOB");
}
Regardless of the inheritance, NH can map different types to the same table. I have done it, albeit without inheritance.
You should be able to remove this line from the NodeEntityLite override and achieve it:
mapping.UseUnionSubclassForInheritanceMapping();
If this proves unsuccessful, you might need to tune the automapping further. It's definitely possible though.

Fluent Nhiberante sub class same table as class

I have two classes:
class User {
public int Id { get;set; }
public string Name { get; set; }
}
class VerifiedUser : User {
public ICollection<Verified> { get; set; }
}
I would like NHibernate to treat VerifiedUser and User as the same table but keep them separate to, so.
Session.Query<User>() //would return a User
Session.Query<VerifiedUser>() //would return a VerifiedUser
Is this possible or is it unsupported?
You will need to implement the table-per-hierarchy strategy with Fluent Nhiberate in mapping classes. These are like overrides for the AutoMapping feature (if used) of FNH, otherwise mapping classes are de facto and you will be used to them.
Something like:
public class UserMappingOverride : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.DiscriminateSubClassesOnColumn("IsVerified").Not.Nullable();
}
}
public class VerifiedUserClassMap : SubclassMap<VerfiedUser>
{
public VerifiedUserClassMap()
{
DiscriminatorValue("Yes");
}
}
And to answer your question, yes as far as I remember nothing to do here: Session.QueryOver<VerifiedUser>() as NHibernate will add on the where clause for the discriminator

Mapping class hierarchy through fluent nhibernate by using 2 strategies

I want to combine table-per-class and table-per-hierarchy strategies using fluent nhibernate or nhibernate itself(I mean hbm files), but I don't know how. I prefer fluent over hbm but if it's impossible, then hbm is also fine. I tested this by introducing Entity as ClassMap and all other as SubClassMap in fluent but then in hbm files generated by fluent, Entity was a class and all other were joined-classes which is not what I want. I will describe the problem in more detail below.
Class hierarchy:
public class Entity
{
public int ID { get; set; }
public string Name { get; set; }
}
public abstract class Person : Entity
{
public string Phone { get; set; }
}
public class SystemUser : Person
{
public string Password { get; set; }
}
I want to have one table for entity and one for person and all kinds of it(all its subclasses).I mean I want to use table-per-class strategy for Entity and table-per-hierarchy strategy for Person and SystemUser classes. Database structure is something like this:
EntityTable(ID(PK),Name)
PersonTable(EntityID(PK,FK),Phone,Password)
any help appreciated.
if EntityTable Id is not database generated (which is discouraged by NH anyways) you can use the trick
public PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonTable");
Id(p => p.Id, "EntityID").GeneratedBy.HiLo("100");
DiscriminateSubClassesOnColumn("PersonType");
Map(x => x.Phone);
Join("EntityTable", join =>
{
join.KeyColumn("ID");
join.Map(p => p.Name);
});
}
}
public SystemUserMap : SubclassMap<SystemUser>
{
public SystemUserMap()
{
Map(x => x.Password);
}
}

How do I exclude a base class using fluent mappings in Fluent NHibernate?

I have an abstract base class, Entity, that all my POCOs derive from:
public abstract class Entity
{
public virtual Guid Id { get; set; }
}
And the mapping file:
public class EntityMap<T> : ClassMap<T> where T : Entity
{
public EntityMap
{
Id(x => x.Id);
}
}
This way, I don't have to write Id(x => x.Id) in every mapping file by using this:
public class Something : EntityMap<T>
{
blahblah
}
I'm auto-generating my database schema, and everything looks fine, except that the Entity base class is added as a table. Using fluent mappings, how do I configure it so that the Entity class is excluded from the database schema?
You can add it to the ignore list of auto mapper:
AutoMap.AssemblyOf<Entity>()
.IgnoreBase(typeof(Entity));

Mapping a derived class with additional collection property in nhibernate

I'm trying to use the table-per-subclass (which fluent-nhibernate automaps by default) with a class structure like the following:
public class Product
{
public virtual int Id{ get; set; }
public virtual string Title{ get; set; }
}
public class ProductPackage : Product
{
public ProductPackage(){ Includes = new List<Product>(); }
public virtual IList<Prodcut> Includes{ get; private set; }
[EditorBrowsable( EditorBrowsableState.Never )]
public class ProductPackageAutoOverride : IAutoMappingOverride<ProductPackage>
{
public void Override( AutoMap<ProductPackage> mapping )
{
mapping.HasManyToMany( x => x.Includes )
.WithTableName( "IncludesXProduct" )
.WithParentKeyColumn( "ProductId" )
.WithChildKeyColumn( "IncludesProductId" )
.Cascade.SaveUpdate();
}
}
}
Instead of adding a new table "IncludesXProduct" to represent the many-to-many mapping, it adds a property "ProductPackageId" to the Product table. Of course persisting to this schema doesn't work.
Have I missed something simple or is this type of thing not really supported by NHibernate?
It is possible to do this with NHibernate. Unfortunately my fluent syntax isn't very good, but it looks like FNH is somehow regarding the relationship as a many-to-one rather than a many-to-many.
If you tag your question with "fluent-nhibernate" then you may get more knowledgeable people answering.