Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NHibernate configuration for uni-directional one-to-many relation

I'm trying to set up a relationship as follows. Each Master item has one or more Detail items:

public class Detail {
    public virtual Guid DetailId { get; set; }
    public virtual string Name { get; set; }
}
public class Master {
    public virtual Guid MasterId { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Detail> Details { get; set; }
}

And Mappings:

public class MasterMap : ClassMap<Master> 
{
    public MasterMap() 
    {
        Id(x => x.MasterId);
        Map(x => x.Name);
        HasMany(x => x.Details).Not.KeyNullable.Cascade.All();
    }
}
public class DetailMap : ClassMap<Detail> 
{
    public DetailMap() 
    {
        Id(x => x.Id);
        Map(x => x.Name);
    }
}

The Master database table is:

masterId   uniqueidentifier NOT NULL
name       nvarchar(max) NULL

and Detail is:

DetailId   uniqueidentifier NOT NULL
name       nvarchar(max) NULL
MasterId   uniqueidentifier NULL
foreign key (masterId) references [Master]

I don't really care to have a link from Detail back to Master -- in otherwords, Detail objects on their own are just not interesting to my domain layer. They will always be accessed via their Master object.

Using code like this:

Master mast = new Master 
{
    MasterId = new Guid(),
    Name = "test",
    Details = new List<Detail> 
    {
        new Detail { .DetailId = new Guid(), .Name = "Test1" },
        new Detail { .DetailId = new Guid(), .Name = "Test1" }
    }
};

using (transaction == Session.BeginTransaction) 
{
    Session.Save(mast);
    transaction.Commit();
}

This works great, except for a crazy limitation outlined in this post: NHibernate does an INSERT and puts Detail.MasterId as NULL first, then does an UPDATE to set it to the real MasterId.

Really, I don't want Detail entries with NULL MasterIds, so if I set the MasterId field to NOT NULL, the INSERT to Detail will fail, because as I said NHibernate is trying to put in MasterId = NULL.

I guess my question boils down to this:

How can I get the above code sample to work with my existing domain model (eg, without adding a Detail.Master property), and the Detail.MasterId field in the database set to NOT NULL?

Is there a way to get Nhibernate to just put the correct MasterId in the initial INSERT, rather than running an UPDATE afterwards? Is there rationale somewhere for this design decision? -- I'm struggling to see why it would be done this way.

like image 958
gregmac Avatar asked Dec 16 '10 22:12

gregmac


People also ask

What is NHibernate mapping?

The hibernate-mapping element allows you to nest several persistent <class> mappings, as shown above. It is, however, good practice to map only a single persistent class, or a single class hierarchy, in one mapping file and name it after the persistent super-class. For example, Cat. hbm. xml, Dog.

What is the difference between NHibernate and fluent NHibernate?

Fluent NHibernate offers an alternative to NHibernate's standard XML mapping files. Rather than writing XML documents, you write mappings in strongly typed C# code. This allows for easy refactoring, improved readability and more concise code.


1 Answers

NH3 and above allow to correct save entities in case of uni-directional one-to-many mapping without annoying save null-save-update cycle, if you set both not-null="true" on <key> and inverse="false" on <one-to-many>

FluentNHibernate code snippet for that:

public class MasterMap : ClassMap<Master> 
{
    public MasterMap() 
    {
        Id(x => x.MasterId);
        Map(x => x.Name);
        HasMany(x => x.Details)
            .Not.Inverse()     //these options are very
            .Not.KeyNullable() //important and work only if set together 
            .Not.KeyUpdate()   //to prevent double update
            .Cascade.All();
    }
}
like image 70
hazzik Avatar answered Sep 24 '22 02:09

hazzik