Fluent NHibernate Inheritance Issue - fluent-nhibernate

I'm using Nhibernate with Fluent and I encounter an issue with inheritance.
Here my DB schema
TABLE Base
IDBASE (PK)
Field1
TYPE
TABLE T1
IDBASE (PK and FK)
Field2
Field3 ...
My mapping files are :
public class BaseMap: ClassMap<BASE>
{
public BaseMap()
{
Id(x => x.Id, "IDBASE").GeneratedBy.Identity();
Map(x => x.Field1);
DiscriminateSubClassesOnColumn("TYPE");
}
}
public class T1Map: SubclassMap<T1>
{
public T1Map()
{
Table("T1");
KeyColumn("IDBASE");
DiscriminatorValue("T1");
Map(x => x.Field2).Not.Nullable();
Map(x => x.Field3).Not.Nullable();
}
}
I use FluentMappings instead of AutoMapping.
Here my entities :
public abstract class BASE
{
public virtual long IdBase{ get; set; }
public virtual string Field1 { get; set; }
}
public class T1: BASE
{
public virtual string Field2 { get; set; }
public virtual string Field3 { get; set; }
}
T1 entity inherits from BASE entity, the issue is when I try to get a row NHibernate try to select Field2 and Field3 on the Base Table whereas they should be selected on T1 Table.
I've tried dozens of hacks but it still doesn't work, if anyone as an idea it would be very helpful.
Thanks a lot.

You're specifying a discriminator, that implies that the inheritance structure should be a table-per-class-hierarchy; you won't have two tables with this setup, you'll have a single table with everything in (hence why the select is hitting the same table for all the columns).
If you remove the DiscriminateSubclassesOnColumn call, that'll put your mappings into a table-per-class, so you'll have your desired structure.

Actually I really need the DiscriminateSubclassesOnColumn so I found an other solution, in my inherited entity I do the mapping with the join like this :
public class T1Map: SubclassMap<T1>
{
public T1Map()
{
Join("T1", y =>
{
y.KeyColumn("IDBASE");
y.Map(x => x.Field2).Not.Nullable();
y.Map(x => x.Field3).Not.Nullable();
});
}
}
And it works fine, I can keep the Discriminator column on my base class.
Thanks

Related

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);
}
}

What is the difference between Fluent Mapping and Auto mapping in Fluent NHibernate

After reading some of the articles about Fluent NHibernate I got confused from where to start
I have an existing database to which I need to create DataAccessLayer. I am new to NHibernate and FluentNhibernate. Since I understood that there is no need to write hbm.xml files, I picked Fluent Nhibernate.
So, What is FluentMapping? and AutoMapping?
I have created a classLibraryProject named FirstProject.Entities
I have created a class named "Customer"
namespace FirstProject.Entities
{
public class Customer
{
public virtual int CustomerID { get; set; }
public virtual string CustomerName { get; set; }
public virtual string Address1 { get; set; }
public virtual string Address2 { get; set; }
public virtual string City { get; set; }
public virtual string State { get; set; }
public virtual int Zip { get; set; }
}
}
Then I created a Mapping class
namespace FirstProject.Entities
{
public class CusotmerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID).Column("CustomerID").GeneratedBy.Assigned();
Map(x => x.CustomerName);
Map(x => x.Address1);
Map(x => x.Address2);
Map(x => x.City);
Map(x => x.Zip);
}
}
}
I now don't know how to proceed further. Am I doing it right.. please suggest
how to configure and proceed further
The following is Fluent
Id(x => x.CustomerID).Column("CustomerID").GeneratedBy.Assigned();
I use Fluent assertions, like the following
actual.Should().BeGreaterThan(1).And().LessThan(2);
Fluent is basically where you chain together the commands such that it reads quite well.
Auto mapping is where you do nothing. Everything is done by conventions. I tend to use Auto. Fluent is nice if you don't follow conventions.
Based on your mapping, the CustomerId being Assigned is not the out-of-the-box convention. As such you need to either
Use Fluent to specify exactly how it should map. This is just like doing it the standard way in XML, but with a fluent interface.
Use Auto and specify a Convention that will automatically change CustomerId to be Assigned.
Use Auto and specify an Override, that will use Auto but override CustomerId to be Assigned.
If you want to do option 3, here is the code:
var model = AutoMap
.AssemblyOf<Customer>()
.Where(IsMapped)
.Override<Customer>(a => a.Id(b => b.CustomerId, "CustomerId").GeneratedBy.Assigned());
The function IsMapped must return True for entities you want to Map.

Fluent NHibernate Many to One Mapping Problem

Edit
I have the answer and will post it up tomorrow.
I have a class that appears to be using the parent primary key column instead of it's own primary key column.
There are two classes involved here ActionHistory and ActionData. The parent class, ActionHistory, represents an action and details who performed the action. The child class, ActionData, represents a piece of data that was created by the action. Each ActionHistroy has a number of ActionData and this is represented through a many to one mapping.
The classes are:
public class ActionHistory
{
public virtual int Code { get; set; }
public virtual string Doneby { get; set; }
public virtual IList<ActionData> _ActionData{ get; set;}
}
public class ActionData
{
public virtual int Code { get; set; }
public virtual double Data{ get; set; }
public virtual ActionHistory _ActionHistory{ get; set; }
}
My mappings are:
public class ActionHistoryClassMap : ClassMap<ActionHistory>
{
public ActionHistoryClassMap ()
{
Table("ACTIONHISTORY");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Doneby, "DONEBY");
HasMany(x => x._Actiondata).KeyColumn("CODE").AsBag().Cascade.All();
}
}
public class ActionDataClassMap : ClassMap<ActionData>
{
public ActionDataClassMap ()
{
Table("ACTIONDATA");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Data, "DATA");
References(x => x._ActionHistory, "ACTIONHISTORYID");
}
}
The problem is when an ActionHistory class is queried the correct ActionHistory is returned but the HistoryData that it contains is wrong. A list of HistoryData should be returned but only one HistoryData is returned and it's Code value is the Code values of the parent ActionHistory class. I believe my problem stems form the fact that both classes have the same primary key column name and I'm not handling this properly. The columns that that are related are CODE from the ACTIONHISTORY table and ACTIONHISTORYID from the ACTIONDATA table. The database is fixed so I can't rename any columns. In this case data is only read from the database and not saved or updated.
I downloaded NHibernate Profiler to help me with this problem and have found the exact point in the SQL where the problem occurs.
This is the SQL query that is generated to return the ActionData (where 18 is the ActionHistory primary key):
SELECT actiondat0_.CODE as CODE1_,
actiondat0_.CODE as CODE9_0_,
actiondat0_.DATA as DATA9_0_,
actiondat0_.ACTIONHISTORYID as ACTIONHI4_9_0_
FROM ACTIONDATA actiondat0_
WHERE actiondat0_.CODE = 18 /* #p0 */
The last line should be:
WHERE actiondat0_.ACTIONHISTORYID = 18 /* #p0 */
But I just do not understand why the mapping is not generating the right query.
I know it was something blindingly obvious and after hours of getting nowhere it of course jumps out at me.
The ActionHistory class map should of course be:
public class ActionHistoryClassMap : ClassMap<ActionHistory>
{
public ActionHistoryClassMap ()
{
Table("ACTIONHISTORY");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Doneby, "DONEBY");
HasMany(x => x._Actiondata).KeyColumn("ACTIONHISTORYID")
.AsBag().Cascade.All();
}
}
The difference being the KeyColumn method is now ACTIONHISTORYID as opposed to CODE. I didn't cop that KeyColumn should reference the ActionHistory key column. It seems obvious now and it didn't help that all other times I implemented it in this project the columns with the relationship always had the same name.

SubClass mapping with multipe "roles"

a tricky problem - please bear with me. Any help greatly appreciated.
I have a table/class Contact (PK Id) and two derived Client and Debtor (PK and FK ContactId). The 4th table Case has foreign keys to Debtor and Client (mappings below).
Everything worked fine at first. But then I hit some data where the same Contact is a Client in one Case but a Debtor in another. If those are read in one nhibernate query like Session.Query<Case>().Fetch(c => c.Debtor).Fetch(c => c.Client)
there is a
NHibernate.WrongClassException
"Object with id: {someGuid...} was not of the specified subclass: Client
(loading object was of wrong class [Debtor])
Seems like the session first level cache is recognizing the record by it's Id and tries to avoid reading the data from the sql result set. Of course the cast NH thinks is necessary for the reuse fails.
Unfortunately changing the DB schema is not an option. It's a legacy system. (an the schema is ok and clean IMO)
Don't know if it is important: The class Contact is not abstract. There are Contacts used who are neither Client nor Debtor.
Is there any chance of getting this to work with these multi-role-contacts? Thanks in advance.
public partial class ContactMap : ClassMap<Contact>
{
public ContactMap()
{
Id(x=>x.Id).GeneratedBy.Guid();
Map(x=>x.FirstName);
Map(x=>x.Name1).Not.Nullable();
...
}
}
public class DebtorMap : SubclassMap<Debtor>
{
public DebtorMap()
{
KeyColumn("ContactID");
Table("[dbo].[Debtor]");
Map(x => x.MaritalStatus);
...
}
}
public partial class ClientMap : SubclassMap<Client>
{
public ClientMap()
{
KeyColumn("ContactID");
Map(x => x.ClientNo).Not.Nullable();
...
}
}
public partial class CaseMap : ClassMap<Case>
public CaseMap()
{
...
References<Client>(x=>x.Client)
References<Debtor>(x=>x.Debtor)
...
}
If you can add a view to the schema, you can create a view called Roles which unions both Client and Debtor records. You can then change your object model to represent roles:
class Contact
{
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual ICollection<Role> Roles { get; private set; }
}
class Role
{
public virtual Guid Id { get; set; }
}
class Client : Role
{
public virtual string ClientNo { get; set; }
}
class Debtor : Role
{
public virtual string MaritalStatus { get; set; }
}
class ContactMap : FluentNHibernate.Mapping.ClassMap<Contact>
{
public ContactMap()
{
Table("dbo.Contacts");
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.FirstName);
HasMany(x => x.Roles)
.KeyColumn("ContactId")
.Not.LazyLoad()
.Fetch.Join();
}
}
class RoleMap : FluentNHibernate.Mapping.ClassMap<Role>
{
public RoleMap()
{
Table("dbo.Roles");
Id(x => x.Id).GeneratedBy.GuidComb();
this.Polymorphism.Implicit();
}
}
class ClientMap : FluentNHibernate.Mapping.SubclassMap<Client>
{
public ClientMap()
{
Table("dbo.Clients");
KeyColumn("Id");
Map(x => x.ClientNo);
}
}
class DebtorMap : FluentNHibernate.Mapping.SubclassMap<Debtor>
{
public DebtorMap()
{
Table("dbo.Debtors");
KeyColumn("Id");
Map(x => x.MaritalStatus);
}
}
Since the Contact table does now share a PK with Client and Debtor tables this should work. The Roles view would look something like this:
create view dbo.Roles as
select
Id,
ContactId
from dbo.Clients
union all
select
Id,
ContactId
from dbo.Debtors

Fluent nHibernate mapping opposite side of References

I've got a table structure similar to the below:
AttributeKey (ID, Title)
AttributeValue (ID, AttributeKeyID, Value)
I've got my mapping all setup so that if I query AttributeValue, it joins to AttributeKey (AttributeValue.AttributeKeyID = AttributeKey.ID) using References, no problem... however, I want to query the AttributeKey table and LEFT JOIN onto the AttributeValue table so that I get all AttributeKeys and any values ascociated to them... how do I map this in fluent nHibernate?
Essentially it's the reverse of a References mapping.
EDIT: I'm aware that the HasMany method is meant to be the reverse of References but using that throws the exception that my property doesn't implement UserCollectionType
Cheers
This should be a HasMany in your AttributeKey ClassMap. Your class should look something like this:
public class AttributeKey
{
public int Id { get; set; }
public string Title { get; set; }
public IList<AttributeValue> AttributeValues { get; set; }
}
Your mapping would then look like this:
public class AttributeKeyMap : ClassMap<AttributeKey>
{
public AttributeKeyMap()
{
Id(x => x.Id, "ID");
Map(x => x.Title);
HasMany(x => x.AttributeValues)
.KeyColumn("AttributeKeyID")
.Inverse()
.Cascade.AllDeleteOrphan();
}
}