views:

1031

answers:

2

I have a problem with one-to-many relationships. I have the following domain classes:

public class Installation : Entity<Installation>
{        
    public virtual string Name { get; set; }
    public virtual IList<Institution> Institutions { get; set; }

    public Installation()
    {
        Institutions = new List<Institution>();
    }
}
public class Institution : Entity
{
    public virtual string Name { get; set; }
    public virtual string Address { get; set; }
    public virtual string City { get; set; }
    public virtual Installation Installation { get; set; }        
}

I have made the Entity base class according to the following post. I have the following mappings defined:

public class InstitutionMapping : ClassMap<Institution> 
{
    public InstitutionMapping()
    {
        WithTable("Institution");
        Id(i => i.Id).GeneratedBy.Guid();
        Map(i => i.Name).Not.Nullable().WithLengthOf(50);
        Map(i => i.Address).Not.Nullable().WithLengthOf(50);
        Map(i => i.City).Not.Nullable().WithLengthOf(50);
        References(i => i.Installation).ColumnName("InstallationId").Not.Nullable().WithForeignKey();
    }
}

public class InstallationMapping : ClassMap<Installation>
{
    public InstallationMapping()
    {
        WithTable("Installation");
        Id(i => i.Id).GeneratedBy.Guid();
        Map(i => i.Name).Not.Nullable().WithLengthOf(50);
        HasMany<Institution>(i => i.Institutions).KeyColumnNames.Add("InstallationId").Inverse().Cascade.All();
    }
}

I unit test adding institutions to an installation in the following way:

Installation installation = TestHelper.CreateAnonymousInstallation();
installation.Institutions.Add(TestHelper.CreateAnonymousInstitution());
installation.Institutions.Add(TestHelper.CreateAnonymousInstitution());
session.Save(installation);    
session.Flush();
session.Clear();
Installation returnedInstallation = session.Get<Installation>(installation.Id);
Assert.AreEqual(2, returnedInstallation.Institutions.Count);

I get an assertion exception because the returned number of institutions is 0. I have checked in the SQL Profiler and the institutions are saved in the database but their InstallationId is null. Could somebody tell me what I am doing wrong?

Thank you in advance. Lukasz Glaz

+1  A: 

You have to manually set the Installation Property of an Institution, specifically,

Installation installation = TestHelper.CreateAnonymousInstallation();
Institution institution = TestHelper.CreateAnonymousInstitution();
institution.Installation = installation;
installation.Institutions.Add(institution);
Canton
Remember, nh does not auto sync bi-directional relationships.
epitka
I know that your solution works. However I thought that in case of normal bi-directional relationship NHibernate should do that automatically. Is there any way to configure the solution so that it is only necessary to add children to the collection without setting the parental reference?
GUZ
Yes. Don't set `inverse="true"`. But then, if you set a child's reference to its parent directly instead of adding the child to the parent's collection, NHibernate may not modify the relationship.
Justice
+2  A: 

When you have a persistent collection with inverse="false", then the parent object owns the relationship and any changes to the parent's collection will be reflected in the database.

When you have a persistent collection with inverse="true", then the child object owns the relationship and any changes to the child's reference to the parent will be reflected in the database.

Because you set inverse="true", you will need to change the child object's reference to the parent object in order for NHibernate to pick up on it. If you wish NHibernate to pick up on the changes to the relationship whenever you add children to or remove children from the parent's collection, you must set inverse="false" on the collection.

Justice
Indeed it solves my problem when there is no Not.Nullable() and when PatronId may be null. If it is not the case when I save the installation in my unit test I get the following error:" NHibernate.PropertyValueException: not-null property references a null or transient value".How to overcome this problem?
GUZ
The answer is: you should always alter both sides of the relationship. Set the child's parent reference and add the child to the parent's collection, every time. Whether you do this in two steps every time or whether you build some sort of transparent way of doing this in one step is up to you.
Justice
I understand. I have one more problem: I am unit testing the mapping using PersistenceSpecification. When verifying mapping ofr the Institution CheckList method also throws NHibernate.PropertyValueException: "not-null property references a null or transient value". How to use PersistenceSpecification when I need to alter both sides of the relationship?
GUZ
You can either map the child's parent reference as `cascade="all"` (in addition to mapping the parent's collection of children as `cascade="all"`) or ensure that you always call `ISession.Save` on the parent before the session is flushed.
Justice