我有多个模型,所有这些模型都在视图模型中。我在控制器中的一种操作方法是用于向数据库添加数据。
我正在使用实体框架进行代码优先的迁移
这些表的主键使用的是Entity Framework 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添加到数据库中时,我希望它成为消息表中的外键。
答案 0 :(得分:0)
您需要确保消息中的“预订”引用指向与您要添加的预订相同的实例。 EF将在那里管理FK协会。
例如:
bvm.Messages.Booking = bvm.Booking; // Associate the same reference.
_context.Booking.Add(bvm.Booking);
_context.Messages.Add(bvm.Messages);
_context.SaveChanges();
我会避免在视图模型中传递Entity类,因为这可能会导致各种问题,因为您可能认为您正在传递一个实体,但实际上您只是传递了一个与POD无关的POCO数据实例。上下文。 EF会将每个反序列化的实例视为一个新引用,即使它们具有匹配的ID。您必须将它们与DbContext关联,并更新对可能已经关联的实例的引用。这很丑陋,容易出错,而且容易受到恶意用户的未经授权的数据操纵。
例如:以采用以下数据的方案为例:
Booking { Id = 0 }, Message { Id = 0, Booking { Id = 0 }}
因此,它接受ID为0的新预订,以及引用我们新预订的新消息。
当我们致电context.Booking.Add(booking)
时,EF将使用自动生成的ID创建新的预订。可以说“ 15”。收到消息时,消息包含一个新的不同的预订参考,因此EF继续插入该参考,并获得ID16。(即使有关预订的其余数据与传入的第一个数据相同)由于这两个预订引用没有指向同一实例,因此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的实体的上下文错误。这是不值得的。 :)