将生成的主键作为外键插入到与控制器不同的表中

时间:2019-05-14 20:48:36

标签: c# asp.net-mvc entity-framework linq entity-framework-6

我有多个模型,所有这些模型都在视图模型中。我在控制器中的一种操作方法是用于向数据库添加数据。

我正在使用实体框架进行代码优先的迁移

这些表的主键使用的是Entity Framework ID,因此它们是自动生成的。

所有这些模型都使用外键相互依赖。我试图同时将数据插入这些模型数据库表中。

如何获取一个表的主键并将其作为外键插入其他表中?

BookingMessagesItems是不同的模型类

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添加到数据库中时,我希望它成为消息表中的外键。

1 个答案:

答案 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的实体的上下文错误。这是不值得的。 :)