Fluent nHibernate - Mapping Children with Composit

2019-05-18 04:12发布

问题:

Given a simple parent -> child (CK,CK) setup like this .. I am having trouble with adding a new child object and it getting the parent reference. So I would add the object in such a way ..

var parent = new Parent{
  Children = new List<Child>{
   new Child{
     Other = otherReference
   }
  }
};

Or even adding it using the Add() method...

parent.Children.Add(new Child { Other = other });

The reference to the Parent does not get pushed through. It just ends up as a null property. I get the following exception.

{"Cannot insert the value NULL into column 'ParentId', table 'mssql_test.Children'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}

I can do this ...

new Child { 
   Parent = parentReference,
   Other = otherReference
}

But that seems a bit redundant. My understanding is that it should be able to infer the reference by itself. If this is not possible, perhaps I am just misunderstanding. Can anyone help me? I have an outline of my code below.

Classes

class Parent {
  int Id { get; set; }
  IList<Child> Children { get; set; }
}
class Other {
  int Id { get; set; }
}
class Child {
  Parent Parent { get; set; }
  Other Other { get; set; }
  // other properties
}

Mapping

 ChildMap() {
      CompositeId()
        .KeyReference(x => x.Parent, "ParentId")
        .KeyReference(x => x.Other, "OtherId");
    }

    ParentMap(){
     HasManyToMany(x => x.Children)
                    .AsBag()
                    .ChildKeyColumns.Add(new[] { "ParentId", "OtherId" })
                    .Inverse()
                    .Cascade.All())
                    .Table("[Test]");
}

回答1:

As @KeithS points out the problem is that you have mapped the Child collection as HasManyToMany when it should be HasMany. Here is how the mapping should look:

  ChildMap() {
      CompositeId() //This is is good
        .KeyReference(x => x.Parent, "ParentId")
        .KeyReference(x => x.Other, "OtherId");
  }

  ParentMap(){ //This is the fix
        HasMany(c => c.Children)
          .Inverse()
          .Cascade.All()
          .KeyColumn("ParentId") //Not needed if Parent Id prop maps to ParentId col
          .Table("[Test]");
  }


回答2:

You are not mapping the Parent as a property of the Child. NHibernate (and FluentNH) only map what you tell them to; you could have a dozen fields on your object, but if you only map one of them, that's all NH will provide when it hydrates an instance for you. You should include a "References" method to the Parent in your mapping, specifying the Parent's key field as the FK reference. That should give you the "backreference" you have in your object hierarchy.

Also, it looks like instead of a many-to-many on the Parent side, you should have just a one-to-many (using HasMany). Parents can have many Children, but a Child has one and only one Parent. ManyToMany may work, but it creates a redundant cross-reference table in between Parent and Child.