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