Явное значение для ошибки столбца идентификаторов при вставке с отношением nHibernate

У меня есть 2 объекта (таблицы) NHibernate, которые находятся в отношениях один ко многим.

Я получаю эту ошибку:

Не удается вставить явное значение для столбца идентификаторов в таблицу «Bookings_Locations», если для параметра IDENTITY_INSERT установлено значение OFF.

при попытке сохранить (вставить) новые записи.

Ошибка (проще увидеть, если открыть изображение в новой вкладке)

Объекты

public class Booking
{
    public virtual int Id { get; set; }

    public virtual void AddBookingLocation(BookingLocation bookingLocationToAdd)
    {
        bookingLocationToAdd.Booking = this;
        this.BookingLocations.Add(bookingLocationToAdd);
    }

    public virtual void RemoveBookingLocation(BookingLocation bookingLocationToRemove)
    {
        this.BookingLocations.Remove(bookingLocationToRemove);
    }

    public virtual IList<BookingLocation> BookingLocations { get; set; }

    public Booking()
    {
        BookingLocations = new List<BookingLocation>();
    }
}

public BookingMap()
{
    Table("Bookings");

    Id(x => x.Id).Column("ID");
    HasMany(x => x.BookingLocations)
       .KeyColumn("ID")
      .Not.LazyLoad().Cascade.All(); 
}

public class BookingLocation
{
    public virtual int Id { get; set; }
    public virtual Booking Booking { get; set; }
}

public BookingLocationMap()
{
    Table("Bookings_Locations");    

    Id(x => x.Id).Column("ID");

    References(x => x.Booking)             
      .Column("ID")             
      .Not.LazyLoad().Nullable()
      .Cascade.All();
}

Значение первичного ключа BookingLocation (ID) равно 0, как только он инициализируется, и я ничего ему не присваиваю. Что наводит меня на мысль, что проблема как-то в картировании отношений.

Я делаю это перед вызовом метода на скриншоте:

Booking.AddBookingLocation(BookingLocation);
this.bookingRepo.insertBooking(Booking, BookingLocation); 

У меня никогда не было таких отношений, так что может быть далеко ...

Любая помощь приветствуется :)

Изменить:

У меня сейчас ошибка:

"В экземпляре объекта не задана ссылка на объект."

...когда запускается SessionFactory.OpenSession.

Это как-то связано с:

public class BookingLocation
{
        public BookingLocation(Booking inBooking)
        {
            this.Booking = inBooking;
            inBooking.AddBookingLocation(this);
        } 
}

... потому что, когда я удаляю конструктор, он запускается (думал, не добавляя BookingLocation)

Я предполагаю, что в то время, когда он открывает базу данных, в конструктор не передается экземпляр, как в вашем примере. Ты знаешь, что я имею в виду?




Ответы (4)



Я думаю, что это не ошибка, специфичная для NHibernate, а ошибка SqlServer - в частности, схема таблицы.

Насколько я понимаю, вы не устанавливаете значения PK на клиенте и ожидаете, что идентификатор будет сгенерирован в базе данных, верно?

Ошибка, которую вы получаете, исходит от SqlServer, указывающего, что таблица настроена для этого, но вы отправляете идентификатор в базу данных.

Включение этого кажется довольно тривиальным.

http://sqlserverplanet.com/sql-server/cannot-insert-explicit-value-for-identity-column-in-table-table-when-identity_insert-is-set-to-off

Но, возможно, это не то, чем вы хотите заниматься.

Этот пример вызывает проблему при вставке в таблицу, содержащую столбец Identity, когда вставка указывает идентификатор, который должен быть вставлен, а не позволяет таблице сгенерировать его. т. е. table1 начинает увеличивать значения с 1, а table2 начинается с 4. В этом примере запись успешно вставляется в таблицу1 (эта строка будет иметь ID:1), а затем пытается вставить ту же строку с идентификатором и всем остальным в таблицу2. Обычно при вставке в таблицу со столбцом Identity вы не указываете идентификатор, так как таблица сгенерирует его за вас.

Возможно, попробуйте установить DefaultValue в сопоставлении на 0 в дополнение к GeneratedBy, предложенному Антоном. Это может помешать NHibernate отправить значение столбца идентификатора, равное 0. Это предполагает, что вы не пытаетесь сгенерировать идентификаторы в приложении, которые должны быть непосредственно вставлены в БД.


ответ на комментарий:

Если классу Booking требуется коллекция BookingLocations, таблице BookingLocation требуется FK, например BookingID. (иначе SELECT * from BookingLocation WHERE BookingID = 123, вы получите список BookingLocations для Booking 123)

Чтобы сопоставить эту связь, ваш BookingMap должен выглядеть так (просто показывает сопоставление коллекции)

HasMany(x => x.BookingLocations)
   .KeyColumn("BookingID") // aka, column in BookingLocation that links a row to this Booking
   .Not.LazyLoad()
   //.Cascade.All() // would advise against this until you can get it working explicitly
   .Inverse() // use this instead
   ;

попробуйте изменить сопоставление ссылок следующим образом:

References(x => x.Booking)             
   .Column("BookingID") // the column in the locations table that points to the booking.
   .Not.LazyLoad()
   ;

Пометка этой коллекции как Cascade.All означает, что вам не нужно сохранять вручную (session.Save) BookingLocation.

И это также означает, что если вы когда-нибудь сохраните Booking, вам нужно убедиться, что вы загрузили BookingLocations, потому что я думаю, что без их загрузки ваша таблица BookingLocation будет выглядеть как коллекция внутри Booking... в этом случае, так как он пуст, он удалит BookingLocations. Точно так же вы, вероятно, захотите удалить Cascade.All из сопоставления Booking References в BookingLocation. т. е. если вы удаляете один объект BookingLocation, вы не хотите удалять связанный с ним объект BookingLocation — могут существовать другие местоположения BookingLocation.

Как правило, я не использую Cascade в своих сопоставлениях, а вместо этого устанавливаю свои коллекции как Inverse(). Однако при использовании обратного сопоставления коллекций BookingLocations не будут сохраняться автоматически при сохранении Booking. Вам придется вернуться к их сохранению вручную:

using (var session = CreateNewSession())
{
    using (var trans = session.BeginTransaction())
    {
        session.Save(myNewBooking)
        myNewBooking.BookingLocations.ForEach(x => session.Save(x));
        trans.Commit();
    }
}

В итоге:


  Bookings (table)
  ID

  Booking_Locations (table)
  ID
  BookingID


  Booking (class)
  ID
  IList<BookingLocation>

  BookingLocation (class)
  ID
  Booking (instance)

Карта бронирования

base.HasMany<BookingLocation>(x => x.BookingLocations)
.KeyColumn("BookingID")
.Inverse()
.Not.LazyLoad()
;

БронированиеРасположениеКарта

base.References<Booking>(x => x.Booking)
.Column("BookingID")
.Not.Nullable()
;

сохранение

var newBooking = new Booking(); // new booking
var newBLocation = new BookingLocation(); // new booking location
newBLocation.Booking = newBooking; // set booking instance on booking location
newBooking.BookingLocations.Add(newBLocation); // add new booking location to booking

session.Save(newBooking); // get the newBooking's ID from the DB insert
session.Save(newBLocation); // save the new location. NHibernate will set the FK col BookingID to the ID of the newly-persisted newBooking, and also get the new ID for newBLocation
person JoeBrockhaus    schedule 28.03.2012
comment
Привет, Джо, спасибо за это. Я не понимаю начало абзаца Этот пример кажется... , но, возможно, это потому, что я пытаюсь сопоставить столбец идентификатора в Booking с столбцом идентификатора в BookingLocation. Это не так, у меня есть внешний ключ в таблице Bookings с именем BookingLocationId. Возможно, здесь я ошибаюсь? - person iKode; 28.03.2012
comment
ОЙ. Я думаю, что ваша модель немного запутана. У вас есть Booking, который содержит набор BookingLocations. Следовательно, ваша таблица Booking не может просто иметь BookingLocationID (это будет 1 к 1) — ваша BookingLocation должна иметь FK, BookingID. Я редактирую свой ответ, чтобы показать код. - person JoeBrockhaus; 28.03.2012
comment
Я переформулировал этот абзац. Ошибка SQL может быть отвлекающим маневром. Модель выглядит так, как будто ее нужно сначала изменить, чтобы правильно отлаживать. Кроме того, если у вас его еще нет, скачайте пробную версию NHibernate Profiler, добавьте ссылку и инициализируйте ее в своем коде, а затем повторите попытку и посмотрите, какой SQL выполняется. Это поможет рассеять облако загадок вокруг того, что NH пытается сделать, гораздо лучше, чем наносить удар в темноте, возясь с нюансами картографических опций. - person JoeBrockhaus; 28.03.2012
comment
хорошо, я после того, как запутался. У меня было это несколько дней назад, действительно столбец bookingId уже существует на BookingLocations. Проблема заключалась в том, что он сохранял идентификатор записи бронирования при вставке вместо поля BookingId, которое также существует в таблице бронирования (старая плохо спроектированная база данных). В любом случае, я думаю, мне нужно новое поле в BookingLocations (для сервера в качестве внешнего ключа - как вы говорите), а не в Bookings (думаю, я запутался). Удивительно, как хорошо nHibernate (и вам) удалось разобраться в моей ерунде ;) Спасибо за совет по профайлеру nHibernate! - person iKode; 28.03.2012
comment
нп. Я только что сделал еще одно редактирование... сопоставление ссылок может быть причиной странного поведения вставки. - person JoeBrockhaus; 28.03.2012
comment
Ты легенда Джо! Большое спасибо за вашу помощь! Я сейчас попробую... - person iKode; 28.03.2012
comment
все еще не могу заставить это работать, я добавил подробности в разделе «Изменить» в исходном сообщении. - person iKode; 28.03.2012

Почему бы не сделать свойство Id типа nullable int? Технически, вы не знаете значение сразу.

person Rich    schedule 28.03.2012

Я знаю, что на вопрос дан ответ, но все равно решил опубликовать, так как столкнулся с той же проблемой с другим решением.

В моем случае nhibernate также жаловался на попытку вставить значения в столбец идентификаторов, даже если идентификатор никогда не был установлен и поэтому приводил к «0» из-за его типа данных (длинный).

Решение было довольно простым... Я забыл включить SaveOrUpdate в транзакцию :-/ Я делал это много раз, но думаю, что это происходит во время поздних ночных сеансов argh

Надеюсь, это поможет кому-то.

Ваше здоровье...

person Martin Horvath    schedule 11.12.2014

person    schedule
comment
Да, я тоже в такой ситуации, и это не работает. Странно, так как это кажется точной проблемой, и до этого у меня было сопоставление XML, которое делает то же самое (насколько я вижу), что не получило эту ошибку - person PandaWood; 11.02.2014