NHibernate Save Is Trying to Clear Child KeyColumn

2019-05-24 07:26发布

问题:

I am trying to create a parent object that has multiple children in a 1 to many relationship. I am not referencing the Parent object on the child object, instead I am mapping its keycolumn as a field.

When I try to save this object for the first time, it works as expected without any issues (cascading all the Id's and children). When I try to get the object from the database, update some properties on it and re-save it again, it fails. The actual error message I am getting is "Could not delete collection".

The error above is due to the fact that it is trying to set the "ParentId" field on the child objects to NULL (which violates the FK constraint I have in the db). If I remove this constraint from the DB, the end result is what I want; however, I do not want it to perform this update (setting parent id to null) at all and I'm not sure why it is. From what I can tell in the SQL code it is generating and sending to the DB, everything else appears to be correct and it would all work if it wasnt for that last update statement.

Obviously, I must have something wrong with my mapping but I cannot figure out what. I tried adding Not.KeyUpdate() but that simply made it not generate a key at all. Does anyone have any ideas what I am doing wrong??

Thanks in advance, I really appreciate it!!!

Please see below for my mapping:

public class Parent
    {
        public Parent()
        {
            Children = new List<Child>();
        }

        public virtual Guid Id { get; set; }

        public virtual IList<Child> Children { get; set; }
    }

    public class Child
    {
        public virtual Guid Id { get; set; }

        public virtual Guid ParentId { get; set; }
    }

    public class ParentMap : ClassMap<Parent>
    {
        public ParentMap()
        {
            Table("Parent");
            Id(x => x.Id);
            HasMany(x => x.Children).KeyColumn("ParentId").Cascade.SaveUpdate().Not.LazyLoad();
        }
    }

    public class ChildMap : ClassMap<Child>
    {
        public ChildMap()
        {
            Table("Child");
            Id(x => x.Id);
            Map(x => x.ParentId);
        }
    }

回答1:

This is caused by the fact, that the Collection of children is not marked as inverse="true".

What you can do is: I. to remove the constraint from DB. NHiberante simply must (without the inverse setting) do 2 steps. Firstly update record to break relation, secondly (due to cascades) alse delete the item

II. Change the mapping, and entities. Like this:

A Child must have reference to the Parent:

public class Child
{
    public virtual Guid Id { get; set; }

    public virtual Guid ParentId { get; set; }
    public virtual Parent Parent { get; set; }
}

Mapping then will be like this:

public class ParentMap : ClassMap<Parent>
{
    public ParentMap()
    {
        Table("Parent");
        Id(x => x.Id);
        HasMany(x => x.Children)
            .KeyColumn("ParentId")
            .Cascade.SaveUpdate()
            .Not.LazyLoad()
            .Inverse() // here we do have the inverse setting
            ;
    }
}

public class ChildMap : ClassMap<Child>
{
    public ChildMap()
    {
        Table("Child");
        Id(x => x.Id);
        Map(x => x.ParentId).Not.Insert().Not.Update(); // this is readonly now
        References(x => x.Parent).Column("ParentId"); // just a Parent is writable
    }
}

Now, you have to all the time properly set the relation in the C# code. I.e. if child is added to Parents collection, it should also get set the Parent reference

parent.Children.Add(child);
child.Parent = parent;

NHibernate will now issue only one statement, to delete child from its table