Search code examples
nhibernatefluent-nhibernatenhibernate-mappingfluent-nhibernate-mapping

Fluent NHibernate One-To-Many Mapping


I have the following 2 classes:

Advert

public virtual int Id { get; set;
public virtual IList<AdvertImage> AdvertImages { get; set; }

AdvertImage

public virtual int Id { get; set; }
public virtual string Filename { get; set;
public virtual Advert Advert { get; set; }

In the DB, my AdvertImages table has the FK 'AdvertId' which relates to the Adverts table which has the PK of 'Id'.

This is a One-To-Many mapping, in that one advert can have many images.

My Fluent NHibernate mappings (edited for brevity) are:

AdvertMap

Id(x => x.Id)
  .GeneratedBy.Identity();
...
HasMany(x => x.AdvertImages)
  .KeyColumn("AdvertId")
  .Inverse();
...
Table("Adverts");

AdvertImageMap

Id(x => x.Id)
  .GeneratedBy.Identity();
...
References(x => x.Advert)
  .Column("AdvertId");
...
Table("AdvertImages");

I am creating a new instance of Advert in code, then populating the AdvertImages property (of Advert) with a List<AdvertImage>.

When I go to persist my Advert object to the DB, I would like the AdvertImages to be inserted into their AdvertImages table, but due to the relationship between the 2 tables, I need the Advert insertion to happen first, so as the PK Id is generated, which can then be inserted in the AdvertImages table. (When I create my list of AdvertImage, I am populating the Filename property, but obviously don't have the new AdvertId at that stage, so want that to be populated when the advert is persisted to the DB).

I have tried experimenting with different Inverse() and Cascade settings but haven't succeeded yet. Can anyone help please?


Solution

  • You need to change your Advert mapping to cascade:

    Id(x => x.Id)
      .GeneratedBy.Identity();
    
    HasMany(x => x.AdvertImages)
      .KeyColumn("AdvertId")
      .Inverse()
      .Cascade.AllDeleteOrphan();
    
    Table("Adverts");
    

    You should then be able to do something like this to persist an Advert object and it's children AdvertImage.

    Advert newAdvert = new Advert();
    AdvertImage newImage = new AdvertImage();
    newImage.Advert = newAdvert;
    newAdvert.AdvertImages.Add(newImage);
    
    using(NHibernate.ISession session = SessionFactory.GetCurrentSession())
    {
        using (NHibernate.ITransaction tran = session.BeginTransaction())
        {
            session.Save(newAdvert);
            tran.Commit();
        }
    }
    

    My entities usually contain Add and Remove methods for bidirectional one to many relationships like this:

    public class Advert
    {
        public virtual IList<AdvertImage> AdvertImages { get; set; }
    
        public virtual void AddImage(AdvertImage newImage)
        {
            newImage.Advert = this;
            AdvertImages.Add(newImage);
        }
    }