I have this entity relationship.
public abstract class UserGroup
{
public virtual int UserGroupId { get; set; }
public virtual int GroupType { get; set; }
public virtual string GroupName { get; set; }
public virtual IList<UserGroupMember> GroupMembers { get; set; }
}
public class UserGroupMember
{
public virtual int UserGroupMemberId { get; set; }
public virtual User User { get; set; }
public virtual UserGroup UserGroup { get; set; }
}
Note that UserGroup
is base class and that I have a few derived classes that are discriminated by UserGroup.GroupType
. I am using the Single Table Per Class Hierarchy inheritance mapping strategy.
Fluent NHibernate mappings:
public class UserGroupMap : BaseClassMap<UserGroup>
{
public UserGroupMap()
{
Table("UserGroup");
Id(x => x.UserGroupId);
DiscriminateSubClassesOnColumn("GroupType");
Map(x => x.GroupName);
HasMany(x => x.GroupMembers)
.KeyColumn("UserGroupId")
.Cascade.AllDeleteOrphan()
.Inverse();
}
}
public class UserGroupMemberMap: BaseClassMap<UserGroupMember>
{
public UserGroupMemberMap()
{
Table("UserGroupMember");
Id(x => x.UserGroupMemberId);
References(x => x.User, "UserId")
.Cascade.SaveUpdate();
References(x => x.UserGroup, "UserGroupId")
.Cascade.SaveUpdate();
}
}
This is what I want to achieve:
I would like to make UserGroupMember a base class also and have it discriminate on the UserGroup.GroupType
discriminator. Is this possible?
Another way of wording this question, it would be great if I could add the following line:
DiscriminateSubClassesOnColumn("UserGroup.GroupType");
Into the UserGroupMemberMap
I. We cannot use DiscriminateSubClassesOnColumn("UserGroup.GroupType");
Among many other good reasons, discriminator
is managed by NHiberante. It means, that such a value must be easily INSERTable during any child object creation. And won't work with external tables/references included...
II. But in these scenarios, we can go another way. Let's oberve the documentation (in xml
mapping, but fluent is just a wrapper on top of it)
<discriminator
column="discriminator_column" (1)
type="discriminator_type" (2)
force="true|false" (3)
insert="true|false" (4)
formula="arbitrary SQL expression" (5)
/>
(1) column (optional - defaults to class) the name of the discriminator column.
(2) type (optional - defaults to String) a name that indicates the NHibernate type
(3) force (optional - defaults to false) "force" NHibernate to specify allowed discriminator values even when retrieving all instances of the root class.
(4) insert (optional - defaults to true) set this to false if your discriminator column is also part of a mapped composite identifier.
(5) formula (optional) an arbitrary SQL expression that is executed when a type has to be evaluated. Allows content-based discrimination.
What is really very interesting and helpful for us is (4)
and (5)
. We can simply set discriminator to be readonly (no insert) and to use formula.
<discriminator insert="false"
formula="(SELECT ug.GroupType FROM UserGroup ug WHERE ug.UserGroupId = UserGroupId)"/>
NOTE: The last UserGroupId
will be by NHibernate treated as a column in current table. Which is essential and awesome...
Good. Now our <discriminator>
does return correct value. It also does not insert a value during Creation of a new instance. We only have to be sure, that there will be assigned proper UserGroup
reference, with required Type...
Fluent should be obvious, but to be sure:
DiscriminateSubClassesOnColumn(null)
.Formula("(SELECT...")
.ReadOnly()
;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With