Fluent nhibernate one-to-many

Christian picture Christian · Oct 27, 2012 · Viewed 7.4k times · Source

I have a one-to-many relationship and cannot get cascade to work, once I set cascade I just get "object references an unsaved transient instance ...".

My mapping looks like this

public class SharedDetailsMapping : ClassMap<SharedDetails>
{
    public SharedDetailsMapping()
    {
        Id(x => x.Id).GeneratedBy.Identity();
        HasMany(x => x.Foos);
    }
}

public class FooMapping : ClassMap<Foo>
{
    public FooMapping()
    {
        Id(x => x.Id).GeneratedBy.Identity();
        References(x => x.SharedDetails).Cascade.SaveUpdate();
    }
}

The classes like this

public class Foo
{
    public Foo()
    {
        SharedDetails = new SharedDetails();
        SharedDetails.Foos.Add(this);
    }

    public Foo(SharedDetails sharedDetails)
    {
        SharedDetails = sharedDetails;
        SharedDetails.Foos.Add(this);
    }

    public virtual Guid Id { get; set; }
    public virtual SharedDetails SharedDetails { get; set; }
}

public class SharedDetails
{
    public SharedDetails()
    {
        Foos = new List<Foo>();
    }

    public virtual Guid Id { get; set; }
    public virtual IList<Foo> Foos { get; set; }
}

I then want to create Foos without having to save the SharedDetails first if its a new Foo, like so:

using (var transaction = _session.BeginTransaction())
{
    var shared = new SharedDetails();

    var fooOne = new Foo(shared);
    _session.SaveOrUpdate(fooOne);

    var fooTwo = new Foo(shared);
    _session.SaveOrUpdate(fooTwo);

    transaction.Commit();
}

Cannot figure out what I have done wrong, it works ofcurse if I save the SharedDetails first but that is why I have Cascade setup.

Answer

moribvndvs picture moribvndvs · Oct 27, 2012

In your SharedDetailsMapping, modify your HasMany to add .Inverse():

public class SharedDetailsMapping : ClassMap<SharedDetails>
{
    public SharedDetailsMapping()
    {
        Id(x => x.Id).GeneratedBy.Identity();
        HasMany(x => x.Foos)
           .Inverse();
    }
}

This instructs NHibernate that Foo owns the relationship, which will help it save objects in the relationship in the right order (in this case, the SharedDetails has to get saved first so we have its ID when Foo is saved).

Further info on the purpose/when to use Inverse: NHibernate's inverse - what does it really mean?

The TL;DNR version:

If you have a bidirectional relationship in your classes (HasMany on one side, References on the other), the HasMany should have .Inverse().