2
votes

Recently I came across a strange behavior in Automapping of Fluent NHibernate. I have the following class structure (some properties cut off for the sake of brewity).

public class UserGroup
{
    public virtual UserGroup ParentGroup { get; set; }

    public virtual UserGroupMember Manager { get; protected set; }

    public virtual ISet<UserGroupMember> Members { get; protected set; }
}

and

public class UserGroupMember : BaseEntity
{
    public virtual User User { get; set; }

    public virtual UserGroup Group { get; set; }
}

The mapping for UserGroup:

public class UserGroupMap : IAutoMappingOverride<UserGroup>
{
    public void Override(AutoMapping<UserGroup> mapping)
    {
        mapping.HasMany(el => el.Members)
            .Cascade
            .AllDeleteOrphan().Inverse().LazyLoad();
    }
}

The automapping creates two column (both of which are foreign keys) in the UserGroupMember table to reflect the relation between UserGroup and UserGroupMembers. I've found out that the generated mapping contains wrong column (as seen below):

<set cascade="all-delete-orphan" inverse="true" lazy="true" name="Members" mutable="true">
    <key>
      <column name="Parent_Id" />
    </key>
    <one-to-many class="Groups.Data.UserGroupMember, Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>

which results in wrong queries:

  1. While insert in UserGroupMember - Group_Id is used (which is right), not using Parent_Id
  2. While select in UserGroupMember - Parent_Id is used

Group_Id is the column in UserGroupMember mapping file which reflects the Group property in UserGroupMember.

I tried to modify the mapping adding .KeyColumn("Group_Id") and it is solves the problem. But is there any way to make Fluent NHibernate 'think the right way'?

1

1 Answers

0
votes

This is from memory, as I don't have test code ready.

When using bidirectional many-to-many, you sometimes have to help FHN figure columns names, if they're not "alike" on both sides.

For example this should map correcly

public class User
{
    public IList<Group> Groups { get; set; }
}

public class Group    
{
    public IList<User> Users { get; set; }
}

While this would not

public class User
{
    public IList<Group> BelongsTo { get; set; }
}

public class Group    
{
    public IList<User> Contains { get; set; }
}

As a rule of thumb, if automapping (with or without conventions) doesn't generate right columns names, especially for non trivial cases, do not hesitate to put an override to set those column names manually.