Variations of this question have been asked and answered many times, and the answers have many overlapping details. I tried so many different things suggested by these answers, but none of them worked in my case.
I have a SQLite database with a parent table and a child table. This is a really easy setup. I am using NHibernate 4.0.4 with code mapping, and not free, as I was suggested, so that the former is newer and better than the latter .
ENTITIES:
public class BillingItem { public virtual int ID { get; set; } public virtual string Name { get; set; } // ... other properties public virtual ICollection<PaymentItem> PaymentItems { get; set; } public BillingItem() { PaymentItems = new List<PaymentItem>(); } } public class PaymentItem { public virtual int ID { get; set; } public virtual BillingItem OwningBillingItem { get; set; } // ... other properties }
BillingItem MAPPING:
public class BillingItemMapping : ClassMapping<BillingItem> { public BillingItemMapping() { Table("BillingItems"); Lazy(true); Id(x => x.ID, map => map.Generator(Generators.Identity)); Set(x => x.PaymentItems, c => { c.Key(k => { k.Column("ID"); k.ForeignKey("BillingItemID"); }); c.Inverse(true); c.Cascade(Cascade.None); }, r => r.OneToMany(o => { })); Property(x => x.Name);
PaymentItem mapping:
public class PaymentItemMapping : ClassMapping<PaymentItem> { public PaymentItemMapping() { Table("PaymentItems"); Lazy(true); Id(x => x.ID, map => map.Generator(Generators.Identity)); ManyToOne(x => x.OwningBillingItem, m => { m.Column("ID"); m.Update(false); m.Insert(false); m.Cascade(Cascade.None); m.Fetch(FetchKind.Join); m.NotFound(NotFoundMode.Exception); m.Lazy(LazyRelation.Proxy); m.ForeignKey("BillingItemID"); }); Property(x => x.DueDate, map => map.NotNullable(true));
WAREHOUSE:
public void Add(BillingItem toAdd) { using (ISession session = Helpers.NHibernateHelper.OpenSession()) using (ITransaction tran = session.BeginTransaction()) { session.Save(toAdd); foreach (var pi in toAdd.PaymentItems) { session.Save(pi); } tran.Commit(); } }
BUSINESS LOGIC:
var bi = new BillingItem() { Name = Guid.NewGuid().ToString(), // ... others.. }; var pi = new PaymentItem() { OwningBillingItem = bi, DueDate = DateTime.Now.AddDays(3) // ... others.. }; bi.PaymentItems.Add(pi); var repo = new Repository(); repo.Add(bi);
As suggested by this answer (and this and this and many others), I set Inverse(true) in my Set ( BillingItemMapping collection) to BillingItemMapping . I also set my bidirectional links in the PaymentItem object:
OwningBillingItem = bi
and BillingItem object:
bi.PaymentItems.Add(pi);
I feel like I'm setting up everything else as it should, and I was busy with a lot of display settings based on suggestions from other sources. However, I just cannot understand why it is not working.
The problem is that I cannot get the foreign key column in the PaymentItem record to store the identifier from BillingItem . If I set the column to not allow NULL (as it should be), I get an exception with a null constraint. If I set it to allow null (for testing), it just gets null (obviously).
What am I doing wrong?