The beginning of the stack trace I got today looked as below:
2009-05-19 11:01:45,078 ERROR [5232] XXX - Speaker already deleted: Castle.ActiveRecord.Framework.ActiveRecordException: Could not perform Delete for Speaker ---> NHibernate.Exceptions.GenericADOException: could not delete collection: [XXX#XXX][SQL: SQL not available] ---> System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'SpeakerId', table 'XXX.dbo.lnk_Session_Speaker'; column does not allow nulls. UPDATE fails. The statement has been terminated. ... at SpeakerProxy3e9fd3b0e82745c2b91b8a353acaa93d.DeleteAndFlush()
Background
I had three tables and classes representing them: Session
/ app_Session
, Speaker
/ app_Speaker
, and SessionSpeaker
/ lnk_Session_Speaker
.
Speaker
declared collection of links session-speaker as follows:
[HasMany(Lazy = true, Table = "lnk_Session_Speaker", ColumnKey = "SpeakerId", Cascade = ManyRelationCascadeEnum.Delete)] public virtual IList SessionSpeakers { get { return _sessions; } set { _sessions = value; } }
The above described problem occured when I tried to delete a speaker who was assigned to a session – there was a row in lnk_Session_Speaker
which represented this connection. Speakers who were not linked could be deleted just fine.
Solution
The reason for the difficulties was lack of Inverse = true
declaration for collection of links session-speaker in Speaker class. This statement means that this side (from Speaker
‘s perspective) is the inverse side – that a Speaker
is pointed by SessionSpeaker
.
[HasMany(Lazy = true, Table = "lnk_Session_Speaker", ColumnKey = "SpeakerId", Cascade = ManyRelationCascadeEnum.Delete, Inverse = true)] public virtual IList SessionSpeakers { get { return _sessions; } set { _sessions = value; } }
With this link objects (rows from lnk_Session_Speaker
table) could be removed first and and then the speaker themselves could be removed.