c# - 在与控制器不同的表中插入生成的主键作为外键
问题描述
我有多个模型,所有这些模型都在一个视图模型中。我在控制器中的一种操作方法用于向数据库添加数据。
我正在使用带有代码优先迁移的实体框架
这些表的主键使用实体框架 ID,因此这些是自动生成的。
所有这些模型都通过外键相互依赖。我正在尝试将数据同时插入到这些模型数据库表中。
如何获取一个表的主键并作为外键插入到不同的表中?
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();
当我将它添加到数据库时,我希望生成的预订 ID 作为消息表中的外键。
解决方案
您需要确保 Message 中的 Booking 引用指向与您正在添加的 Booking 相同的实例。EF 将从那里管理 FK 协会。
例如:
bvm.Messages.Booking = bvm.Booking; // Associate the same reference.
_context.Booking.Add(bvm.Booking);
_context.Messages.Add(bvm.Messages);
_context.SaveChanges();
我会避免在视图模型中传递实体类,因为这可能会导致各种问题,因为您可能认为您正在传递一个实体,但实际上您只是传递了一个与上下文无关的数据的 POCO 实例。EF 会将每个反序列化的实例视为一个新的引用,即使它们具有匹配的 ID。您必须将它们与 DbContext 关联,并更新对可能已经关联的实例的引用。它很丑陋,容易出错,而且还容易被恶意用户未经授权的数据操作。
举个例子:假设一个场景需要以下数据:
Booking { Id = 0 }, Message { Id = 0, Booking { Id = 0 }}
所以它接受一个 ID 为 0 的新预订,以及一条引用我们新预订的新消息。当我们调用context.Booking.Add(booking)
EF 时,将使用自动生成的 ID 创建一个新的预订。比方说,“15”。当它到达消息时,消息包含一个新的、不同的预订参考,所以 EF 去插入它,它得到 Id 16。(即使关于预订的其余数据与传入的第一个相同)因为这两个预订参考不指向同一个实例,所以它们也被 EF 视为两个完全独立的实例。通过在我们的方法中将消息的预订参考设置为第一个,当 EF 更新预订时,消息的预订参考将指向第一个的 ID。
为了避免像这样的各种丑陋,我们要避免重复引用。如果我们有一个插入带有可选消息的新预订的方法,那么与其传递造成此类问题的实体,不如传递常规 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 参考将被自动填写。
当您进入编辑场景等时,继续传递实体引用将导致重复数据更加痛苦,并且已经跟踪具有匹配 ID 的实体的上下文错误。这不值得一团糟。:)
推荐阅读
- java - Android Camera2 API 图像色彩空间
- visual-studio-code - 如何将 VSCode 扩展中的文件复制到工作区?
- python - 为什么即使我将级别设置为警告,我的 python 程序仍会写入调试级别日志?
- javascript - 为什么在 setState 中通过渲染运行 onClick 函数?
- xamarin - Xamarin 动画 ViewAnimations 不起作用
- c++ - 可变模板张量类
- c - 如何根据c中另一个循环的计数器编写一个循环n次的循环
- java - FutureTask 实现重新读取状态以防止泄漏中断
- php - 如何在 php 中添加这些结果并获得平均值
- html - 角度百分比和总计算