3
votes

I'm using the new Map by Code pieces in NHibernate. My understanding was that an update was made in NHibernate 3 so that unidirectional one-to-many relationships would no longer insert null on the foreign key then update it to the correct value, as long as you set inverse=false on the collection and made the foreign key not nullable.

What I'm seeing is that NHibernate now INSERTs the correct foreign key, but it still issues an additional UPDATE that sets the foreign key to the value that was used in the insert?!?

Have I done something incorrectly in my mapping? (A user can have many passwords. The password object does not reference back to the user in my domain.)

mapper.Class<Password>(map =>
{
    map.Table("Passwords");
    map.Id(x => x.Id, x => { x.Generator(Generators.Native); x.Column("PasswordId"); });
    map.Property(x => x.PasswordFormat, x => { x.NotNullable(true); });
    map.Property(x => x.Salt, x => { x.Length(100); });
    map.Property(x => x.PasswordValue, x => { x.NotNullable(true); x.Length(500); });
    map.Property(x => x.CreateDate, x => { x.NotNullable(true); });
});

mapper.Class<User>(map =>
{
    map.Table("Users");
    map.Id(x => x.Id, x => { x.Generator(Generators.Native); x.Column("UserId"); });
    map.Property(x => x.UserName, x => { x.NotNullable(true); x.Length(100); x.UniqueKey("UX_Users_Username"); });
    map.Property(x => x.Email, x => { x.Length(100); x.Index("IX_Users_Email"); });
    map.Property(x => x.IsAnonymous, x => { x.NotNullable(true); });
    map.Property(x => x.IsApproved, x => { x.NotNullable(true); });
    map.Property(x => x.LastActivityDate, x => { x.NotNullable(true); });
    map.Property(x => x.CreateDate, x => { x.NotNullable(true); });
    map.Set(x => x.Passwords, x => { x.Access(Accessor.Field); x.Inverse(false); x.Key(k => { k.Column("UserId"); k.NotNullable(true); k.ForeignKey("FK_Passwords_UserId"); }); x.Cascade(Cascade.All); x.Lazy(CollectionLazy.Lazy); }, x => x.OneToMany());
});

Note: This is using built-in NHibernate.Mapping.ByCode, not Fluent NHibernate.

2
Do you have a link to the issue that was fixed in NH3? I've always done bi-directional 1-to-many for this exact issue... - dotjoe
Here is a link to what I was referencing. See the edit, as well as the comment from Hazzik regarding the issue. stackoverflow.com/a/4469471/139694 - Sam
hmm I remember that question as I upvoted VikciaR's comment...lol, I couldn't get it work - dotjoe
Yup, I up voted that one too :) I haven't been able to find any additional info on NhForge or anywhere else, but I did notice that the suggested changes did result in proper inserts. I guess if it is going to do the updates, I at least want it to maintain referential integrity by avoiding null inserts. I sure would like to be able to avoid adding a property to the Password side of the relationship, but I just might do it to avoid the update. (How many times have you ever re-assigned an encrypted password to another user?) - Sam
never, lol...for these cases I usually end up giving the parent (User) a protected setter and add an extra ctor with the parent as a parameter. Pretty clear it can't be changed then....just my 2 cents. - dotjoe

2 Answers

2
votes

Turns out I can accomplish this by setting k.Update(false) on the foreign key portion of the Passwords collection mapping.

The answer from hazzik on the following question queued me in.

https://stackoverflow.com/a/11576097/139694

1
votes

It should be Inverse() not Inverse(false).

Inverse() means that other entity owns the relationship and it is responsible for providing data for NHibernate about the relationship while inserting/updating information i.e., if "user" is set to on inverse, "password" needs to take care of providing relationship info. to NHibernate.

For this you need to set up "User" reference property on "Password" entity. And while creating/updating the password, assign user property explicitly.

//create new password
Password objPassword = new Password();
objPassword.otherproperties =///assign
objPassword.User = <<---assign the user property

Currently, you have Inverse(false) which is default setting for NHibernate. In this case, insert statements will be executed with passwordid as null and then references are updated resulting in two operations.