Hello.
My schema looks like this:
The Message.Id column is an identity(1,1), and the MessageHeader table uses a composite key consisting of the associated message ID, and a header name field that uniquely identifies a header within each message.
Entities are represented in code as:
public class Message {
public virtual int Id { get; set; }
public virtual string Sender { get; set; }
public virtual string Recipient { get; set; }
private IList<MessageHeader> headers = new List<MessageHeader>();
public virtual IList<MessageHeader> Headers {
get { return headers; }
set { headers = value; }
}
public virtual void SetHeader(string headerName, string headerText) {
MessageHeader header = this.Headers.FirstOrDefault(h => h.HeaderName == headerName);
if (header == default(MessageHeader)) {
header = new MessageHeader() {
HeaderName = headerName,
Message = this
};
this.Headers.Add(header);
header.HeaderText = headerText;
}
}
}
public class MessageHeader {
public virtual Message Message { get; set; }
public virtual string HeaderName { get; set; }
public virtual string HeaderText { get; set; }
public virtual byte[] Version { get; set; }
public override bool Equals(object obj) {
if (Object.Equals(this, obj)) return (true);
var that = obj as MessageHeader;
if (that == null) return (false);
return (this.HeaderName == that.HeaderName && this.Message.Equals(that.Message));
}
public override int GetHashCode() {
return (this.HeaderName.GetHashCode() ^ this.Message.GetHashCode());
}
}
I want to map these entities using Fluent NHibernate so that when I run code like:
var message = new Message() { Sender = "alf", Recipient = "bob" };
message.SetHeader("DateSent", DateTime.Now.ToString());
using (var session = NHibernateHelper.GetCurrentSession()) {
using(var tx = session.BeginTransaction()) {
session.Save(message);
tx.Commit();
}
}
NHibernate will:
- INSERT the Message entity and retrieve the identity value.
- INSERT the MessageHeader entity (with the appropriate MessageId retrieved in step 1)
I cannot make this work. It either tries to insert the MessageHeader first (which fails with a foreign key violation), or it tries to UPDATE the MessageHeader instead of INSERTing it, which returns no rows and throws a "could not synchronise database state with session" error.
My FluentNH mapping overrides are as follows:
public class MessageOverrides : IAutoMappingOverride<Message> {
public void Override(AutoMapping<Message> map) {
map.HasMany(message => message.Headers).KeyColumn("MessageId");
}
}
public class MessageHeaderOverrides : IAutoMappingOverride<MessageHeader> {
public void Override(AutoMapping<MessageHeader> map) {
map.IgnoreProperty(header => header.Message);
map.CompositeId()
.KeyReference(header => header.Message, "MessageId")
.KeyProperty(header => header.HeaderName);
map.Version(header => header.Version)
.CustomSqlType("timestamp").Nullable()
.Generated.Always();
}
}
I thought adding the explicit Version timestamp to the MessageHeader table would resolve this, but it would appear not... I'm sure something needs to be Inverse or Cascade or something, but I am completely stumped as to what needs to go where.
Thanks,
Dylan