Как сопоставить коллекцию has-many, содержащую ссылку на составной ключ, в Fluent NHibernate? - PullRequest
0 голосов
/ 12 октября 2010

Моя схема выглядит так:

database schema diagram

Столбец Message.Id является идентификатором (1,1), а таблица MessageHeader использует составной ключ, состоящий из связанного идентификатора сообщения и поля имени заголовка, которое однозначно идентифицирует заголовок в каждом сообщении.

Объекты представлены в коде как:

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());
    }
}

Я хочу отобразить эти объекты, используя Fluent NHibernate, чтобы при запуске кода, например:

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 будет:

  1. ВСТАВИТЬ сущность Message и получить значение идентификатора.
  2. ВСТАВИТЬ сущность MessageHeader (с соответствующим MessageId, полученным на шаге 1)

Я не могу заставить эту работу. Он либо пытается сначала вставить MessageHeader (который завершается неудачно с нарушением внешнего ключа), либо пытается ОБНОВИТЬ MessageHeader вместо ВСТАВКИ его, что не возвращает строк и выдает ошибку «Не удалось синхронизировать состояние базы данных с сеансом». *

Мои переопределения отображения FluentNH следующие:

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();
    }
}

Я думал, что добавление явной метки времени Version в таблицу MessageHeader решит эту проблему, но это не так ... Я уверен, что что-то должно быть Inverse или Cascade или что-то еще, но я совершенно озадачен тем, что иди куда.

Спасибо

Dylan

Ответы [ 2 ]

0 голосов
/ 13 октября 2010

Решение оказалось - через @mikehadlow в Twitter - что мне нужно .Cascade.SaveUpdate () для отображения HasMany:

public void Override(AutoMapping<Message> map) {
    map.HasMany(message => message.Headers).KeyColumn("MessageId").Cascade.SaveUpdate()
}

, но также оказалось, что не-NHошибка в другом месте кода препятствовала правильной работе.Исправил ошибку, отобразил ее, как показано выше, и она заработала.Свойство Version в MessageHeader удобно, но не обязательно - если вы его опустите, NH запросит БД перед вставкой / обновлением, чтобы выяснить, какой из них требуется.

0 голосов
/ 12 октября 2010

Это выстрел в темноте, пока я не доберусь до машины, где смогу воспроизвести вашу проблему ...

Если ваш HasMany не использует оба столбца для ссылки на заголовки?

В зависимости от вашей версии Fluent NHibernate это, вероятно, будет:

map.HasMany(x=> x.Headers)
  .KeyColumns.Add("MessageId", "HeaderName");

или

map.HasMany(x=> x.Headers)
  .Key(ke =>
  {
     ke.Columns.Add("MessageId", "HeaderName");
  });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...