Fluent Nhibernate HasMany mapping issue

2019-06-06 18:51发布

问题:

I'm new to Fluent and NHibernate and I'm not sure how to map this specific relationship that exists in my database. I have the following ER diagram below that outlines my table structure.

Below are my current C# entity classes:

public class GroupHeader
{
    public virtual Guid Id { get; private set;}
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    public virtual IList<RightHeader> Rights { get; set; }
    public virtual IList<GroupRight> GroupRights { get; set; }
    public GroupHeader()
    {
        GroupRights = new List<GroupRight>();
        Rights = new List<RightHeader>();
    }
    public GroupHeader(string name, string description, IList<RightHeader> rights)
        : this()
    {
        Name = name;
        Description = description;
        Rights = rights;

        if (rights != null)
            foreach (RightHeader right in rights)
                AddRight(right, 1);
    }

    public virtual void AddRight(RightHeader newRight, decimal rightValue)
    {
        GroupRight newGroupRight = new GroupRight(this, newRight, rightValue);
        GroupRights.Add(newGroupRight);
    }
}

public class GroupRight
{
    public virtual GroupHeader Group { get; set; }
    public virtual RightHeader Right { get; set; }
    public virtual decimal RightValue { get; set; }

    public GroupRight()
    {

    }
    public GroupRight(GroupHeader group, RightHeader right, decimal rightValue)
    {
        Group = group;
        Right = right;
        RightValue = rightValue;
    }
}

public class RightHeader
{
    public virtual decimal Num { get; private set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    public virtual IList<GroupRight> GroupRights { get; set; }

    public RightHeader()
    {

    }
    public RightHeader(decimal num, string name, string description)
    {
        Num = num;
        Name = name;
        Description = description;
    }
}

These are my fluent mappings:

public class GroupHeaderMap : ClassMap<GroupHeader>
{
    public GroupHeaderMap()
    {
        Table("GROUP_HEADER");
        Id(x => x.Id, "GROUP_ID");
        Map(x => x.Name, "GROUP_NAME").Unique();
        Map(x => x.Description, "GROUP_DESCRIPTION");
        HasMany(x => x.GroupRights)
            .Table("GROUP_RIGHT_COMPOSITE")
            .KeyColumns.Add("GROUP_ID")
            .Cascade.AllDeleteOrphan();
    }
}

public class GroupRightMap : ClassMap<GroupRight>
{
    public GroupRightMap()
    {
        Table("GROUP_RIGHT_COMPOSITE");
        CompositeId()
            .KeyReference(x => x.Group, "GROUP_ID")
            .KeyReference(x => x.Right, "RIGHT_NUM");

        Map(x => x.RightValue, "RIGHT_VALUE").Not.Nullable();
    }
}

public class RightHeaderMap : ClassMap<RightHeader>
{
    public RightHeaderMap()
    {
        Table("RIGHT_HEADER");
        Id(x => x.Num, "RIGHT_NUM");
        Map(x => x.Name, "RIGHT_NAME");
        Map(x => x.Description, "RIGHT_DESCRIPTION");

        HasMany(x => x.GroupRights)
            .Inverse()
            .Table("GROUP_RIGHT_COMPOSITE")
            .KeyColumn("RIGHT_NUM");
    }
}

Whenever I run my unit test that attempts to add a new GroupHeader, GroupRight and RightHeader to my database I get the following error:

NHibernate.Exceptions.GenericADOException: could not insert: [Business.Objects.GroupRight#Business.Objects.GroupRight][SQL: INSERT INTO GROUP_RIGHT_COMPOSITE (RIGHT_VALUE, GROUP_ID, RIGHT_NUM) VALUES (?, ?, ?)] ---> System.Data.SqlClient.SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_GROUP_RIGHTS_RIGHTS". The conflict occurred in database "TEST", table "dbo.RIGHT_HEADER", column 'RIGHT_NUM'. The statement has been terminated.

I'm pretty sure this happens because the RIGHT_HEADER record that I'm creating is not in the database and it's violating the foreign key constraint but I'm not sure how to map this so that NHibernate will insert the RIGHT_HEADER first before attempting to create the GROUP_RIGHT_COMPOSITE record.

EDIT: The unit test I'm using is as follows:

    [TestMethod()]
    public void GroupHeaderAddTest()
    {
        RightHeader newRight = new RightHeader(1, "Some Right", "Allows user to do something");
        GroupHeader newGroup = new GroupHeader("Administrators", "Administrative group", null, null);
        newGroup.AddRight(newRight, 1);

        using (NHibernate.ISession session = SessionOrigin.Current.GetSession())
        {
            using (NHibernate.ITransaction tran = session.BeginTransaction())
            {
                session.SaveOrUpdate(newGroup);
                tran.Commit();
            }

            GroupHeader foundGroup = session.CreateCriteria<GroupHeader>()
                                .Add(Example.Create(newGroup))
                                .UniqueResult<GroupHeader>();

            Assert.IsNotNull(foundGroup, "Group should not be null");
            Assert.AreEqual<GroupHeader>(newGroup, foundGroup);
        }
    }

回答1:

The only way I could get this to work was by changing my mapping for GroupRight to the following:

public class GroupRightMap : ClassMap<GroupRight>
{
    public GroupRightMap()
    {

        Table("GROUP_RIGHT_COMPOSITE");
        CompositeId()
            .KeyReference(x => x.Group, "GROUP_ID")
            .KeyReference(x => x.Right, "RIGHT_NUM");

        References(x => x.Group, "GROUP_ID")
            .Not.Update()
            .Not.Insert()
            .Cascade.All();

        References(x => x.Right, "RIGHT_NUM")
            .Not.Update()
            .Not.Insert()
            .Cascade.All();

        Map(x => x.RightValue, "RIGHT_VALUE").Not.Nullable();
    }
}