Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use Fluent NHibernate to discriminate on a column of a parent relationship

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

like image 214
Con Kouroupis Avatar asked Oct 15 '14 00:10

Con Kouroupis


1 Answers

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)

5.1.6. discriminator

<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()
    ;
like image 69
Radim Köhler Avatar answered Nov 12 '22 11:11

Radim Köhler