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

C# 将生成的主键作为外键插入控制器的不同表中,c#,asp.net-mvc,entity-framework,linq,entity-framework-6,C#,Asp.net Mvc,Entity Framework,Linq,Entity Framework 6,我有多个模型,所有这些模型都在一个视图模型中。我在controller中的一个操作方法用于向数据库添加数据 我使用实体框架进行代码优先迁移 这些表的主键使用实体框架ID,因此它们是自动生成的 所有这些模型都通过外键相互依赖。我试图同时将数据插入这些模型数据库表中 如何获取一个表的主键并将其作为外键插入到不同的表中 预订、消息和项目是不同的车型类别 预订模型课 public class Booking { [Key] [Column("lngBookingID")] pub

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

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

这些表的主键使用实体框架ID,因此它们是自动生成的

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

如何获取一个表的主键并将其作为外键插入到不同的表中

预订
消息
项目
是不同的车型类别

预订
模型课

public class Booking
{
    [Key]
    [Column("lngBookingID")]
    public Int32 BookingID { get; set; }
    public double BookingCost { get; set; }
 }
消息
型号类别:

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

您需要确保消息中的预订引用指向与您正在添加的预订相同的实例。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#view模型,然后在服务器上处理实体创建(或加载)。传递到像这样的方法中的实体实际上只是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();
}
更好的做法是预订实体拥有一组消息。我怀疑您最初可能尝试过这种方法,但在尝试将预订传递给客户端时遇到了序列化问题。(不通过实体的另一个原因)

这利用了实体之间的关系,因此上下文不必将每个实体视为顶级实体。保存预订后,其消息也将被保存,FK参考信息将自动填写


当您进入编辑场景或类似场景时,继续传递实体引用将导致更多重复数据的问题,以及已跟踪具有匹配ID的实体的上下文错误。这不值得搞得一团糟。:)

您需要确保消息中的预订引用指向与您正在添加的预订相同的实例。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

为了避免像这样的丑陋,我们希望避免重复引用。如果我们有一个方法插入一个带有可选消息的新预订,那么