Вставить сгенерированный первичный ключ как внешний ключ в другую таблицу из контроллера - PullRequest
0 голосов
/ 14 мая 2019

У меня есть несколько моделей, и все эти модели представлены в виде модели.Один из моих методов действия в контроллере используется для добавления данных в базу данных.

Я использую Entity Framework с миграциями с первым кодом

Первичные ключи этих таблиц используют идентификаторы Entity Framework, поэтому ониавтоматически генерируются.

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

Как я могу получить первичный ключ одной таблицы и вставить как внешний ключ в другую таблицу?

Booking, Messages и Items - это различные классы моделей

Booking класс моделей

public class Booking
{
    [Key]
    [Column("lngBookingID")]
    public Int32 BookingID { get; set; }
    public double BookingCost { get; set; }
 }

Messages класс моделей:

public class Messages
{
    [Key]
    [Column("lngMessageID")]
    public Int32 MessageID { get; set; }

    public string MessageSubject { get; set; }
    [ForeignKey("Booking")]
    public Int32 lngBookingID { get; set; }

    public Booking Booking { get; set; }
}

Код дляметод действия.BookingViewModel bvm содержит все необходимые данные:

 _context.Booking.Add(bvm.Booking);
 _context.Messages.Add(bvm.Messages);
 _context.PetInformation.Add(bvm.items);
 _context.SaveChanges();

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

1 Ответ

0 голосов
/ 15 мая 2019

Необходимо убедиться, что ссылка на бронирование в сообщении указывает на тот же экземпляр, что и добавляемое вами бронирование. EF будет управлять ассоциацией FK оттуда.

Например:

bvm.Messages.Booking = bvm.Booking; // Associate the same reference.
_context.Booking.Add(bvm.Booking);
_context.Messages.Add(bvm.Messages);
_context.SaveChanges();

Я бы не стал передавать классы Entity в модель представления, так как это может привести к всевозможным проблемам, так как вы можете подумать, что вы передаете объект, но на самом деле вы просто передаете экземпляр POCO-данных, который не связан с контекст. EF будет обрабатывать каждый десериализованный экземпляр как новую ссылку, даже если у них совпадают идентификаторы. Вы должны связать их с DbContext и обновить ссылки на экземпляры, которые уже могут быть связаны. Это уродливо, подвержено ошибкам, а также склонно к несанкционированному манипулированию данными со стороны злоумышленников.

Например: возьмите сценарий, который принимает следующие данные: Booking { Id = 0 }, Message { Id = 0, Booking { Id = 0 }} Таким образом, он принимает новое бронирование с идентификатором 0 и новое сообщение со ссылкой на наше новое бронирование. Когда мы позвоним context.Booking.Add(booking) EF создаст новое бронирование с автоматически сгенерированным идентификатором. Скажем «15». Когда оно доходит до сообщения, сообщение содержит новую, другую ссылку на бронирование, поэтому EF отправляет и вставляет его и получает Id 16. (Хотя остальные данные о бронировании идентичны первым переданным в) Поскольку две ссылки на бронирование не указывают на один и тот же экземпляр, EF также рассматривает их как два совершенно разных экземпляра. При установке ссылки на бронирование для сообщения на первый в нашем методе, когда EF обновляет бронирование, ссылка на бронирование сообщения будет указывать на идентификатор этого первого.

Чтобы избежать подобных уродств, мы хотим избежать повторяющихся ссылок. Если у нас есть метод, который вставляет новое бронирование с необязательным сообщением, то вместо передачи сущностей, которые создают проблемы, подобные этой, передайте обычные модели представления C # и затем обработайте создание (или загрузку) сущности на сервере. Объекты, передаваемые в такие методы, по сути являются просто классами POCO, но они содержат намного больше данных, чем вы не должны «доверять» обновлению в базе данных. Передавая отдельную модель представления, мы применяем лучшие методы: Загрузка существующих ссылок на сущности из текущего DBContext и / или создание и связывание сущностей на основе предоставленных данных.

Таким образом, метод, который создает новое бронирование с дополнительным сообщением, может выглядеть так:

public ActionResult AddBooking(BookingViewModel booking, MessageViewModel message)
{
   var newBooking = new Booking { BookedDate = booking.BookedDate, /* ... */ };
   _context.Bookings.Add(newBooking);
   if (message != null)
   {
     var newMessage = new Message { MessageText = message.Text, Booking = newBooking };
     _context.Messages.Add(newMessage);
   }
   _context.SaveChanges();
}

Еще лучше было бы, чтобы у объекта бронирования была коллекция сообщений. Я подозреваю, что вы, вероятно, попробовали это изначально, но столкнулись с проблемами сериализации при попытке передать заказы клиенту. (Еще одна причина не пропускать сущности)

public ActionResult AddBooking(BookingViewModel booking, MessageViewModel message)
{
   var newBooking = new Booking { BookedDate = booking.BookedDate, /* ... */ };
   if (message != null)
   {
     var newMessage = new Message { MessageText = message.Text, Booking = newBooking };
     newBooking.Messages.Add(newMessage);
   }
   _context.Bookings.Add(newBooking);
   _context.SaveChanges();
}

Это усиливает отношения между сущностями, так что контекст не должен рассматривать каждую сущность как сущность верхнего уровня. Когда бронирование сохранено, его сообщения также будут сохранены, и ссылки FK будут заполнены автоматически.

По мере того, как вы будете входить в сценарии редактирования и тому подобное, продолжение передачи ссылок на сущности приведет к еще большему затруднению с дублирующимися данными и ошибкам контекста для сущностей с совпадающими идентификаторами, которые уже отслеживаются. Это не стоит путаницы. :)

...