Search code examples
c#nhibernatenhibernate-mapping

NHibernate not inserting children


I'm building a MultiLingual application, with some dynamic translations in it. Therefor I've chosen to make separate tables for the products, that include the translations.

No I'm having trouble to insert the translations, they are a part of the product. Which is inserted.

  <class name="Product" table="Product">
    <id name="Id">
      <generator class="native" />
    </id>
    <property name="SomeOtherStuff" column="SomeOtherStuff" />
    <set name="NaamTranslations" cascade="all">
      <key column="ProductId" not-null="true" />
      <one-to-many class="ProductTranslation" />
    </set>
  </class>

  <class name="ProductTranslation"
         table="ProductTranslation" lazy="false">
    <id name="Id">
      <generator class="native" />
    </id>
    <many-to-one name="Product" column="ProductId" not-null="true" 
                 index="UQ_ProductTranslation" class="Product"
                 cascade="all-delete-orphan" />
    <property name="CultureCode" column="LanguageCode" 
              not-null="true" index="UQ_ProductTranslation" />
    <property name="Name" column="Name" not-null="true" />
  </class>

My guess is that the problem is somewhere in the configuration of NHibernate. As I can follow the creation of the product throughout the code. Also I've put the show_sql on, and it is showing the creation of the Product, but it is lacking the inserts of the ProductTranslations.

INSERT INTO dbo.Product (SomeOtherStuff) VALUES (@p0); 
 select SCOPE_IDENTITY(); @p) = 'Hello this is a test' [Type: String (4000)]

Solution

  • The above mapping should be working. It will just generate not always wanted SQL statements.

    1. Insert into parent Product
    2. Insert into child table ProductTranslation, without reference to Product table (the ProductId column must be nullable)
    3. UPDATE the child table with ProductIdreference

    So, this a bit complicated SQL statements will work even for this kind of C# assignment

    var p = new Product();
    var tr1 = new ProductTranslation();
    var tr2 = new ProductTranslation();
    p.NaamTranslations.Add(tr1);
    p.NaamTranslations.Add(tr2);
    
    session.Save(p);
    

    To avoid those inserts and updates, and have just one INSERT, we can use inverse mapping. That would be just like this

    <class name="Product" table="Product">
        ...
        <set name="NaamTranslations" cascade="all" inverse="true">
    

    but now we have to set both sides of the reference in C#

    var p = new Product();
    var tr1 = new ProductTranslation();
    var tr2 = new ProductTranslation();
    p.NaamTranslations.Add(tr1);
    p.NaamTranslations.Add(tr2);
    
    // too soon - relation back to product is missing
    //session.Save(p);
    
    // assign reference 
    tr1.Product = p;
    tr2.Product = p;
    // now cascade will work
    session.Save(p);
    

    NHibernate will now:

    1. Insert into parent
    2. Insert full record into child (no update later)