为什么我的Seed()方法没有正确保存和连接实体
本文关键字:确保 保存 实体 连接 Seed 我的 方法 为什么 | 更新日期: 2023-09-27 18:23:34
我正在为我的组织建立一个会议注册网站,有多名贵宾和特邀发言人。要求跟踪每位与会者的许多详细信息,包括他们的到达和离开计划以及他们在当地的住宿信息。为了便于与利益相关者讨论我们需要构建的报告类型,我想用CSV中的一批记录填充我的开发数据库,其中包含随机生成的信息,如姓名、到达/离开日期/时间等。这将使我们能够查看工作现场,而无需多次注册和重新注册。
然而,我只是无法获得Seed方法来正确保存相关记录,但我处理注册的控制器工作得很好。
我的数据库结构基本上是一个Attendee实体,具有TravelSchedule
、LodgingArrangement
和各种查找的子实体。以下是我的实体摘录:
public class Attendee
{
public int Id { get; set; }
... other strings/etc ...
public virtual TravelSchedule TravelSchedule { get; set; }
public int TravelScheduleId { get; set; }
public virtual LodgingArrangment LodgingArrangement { get; set; }
public int LodgingArrangementId { get; set; }
}
public class TravelSchedule
{
public int Id { get; set; }
... other properties ...
public virtual Attendee Attendee { get; set; }
public int AttendeeId { get; set; }
}
public class LodgingArrangement
{
public int Id { get; set; }
... other properties ...
public virtual Attendee Attendee { get; set; }
public int AttendeeId { get; set; }
}
以下是我的上下文的OnModelCreating
方法的内容:
modelBuilder.Entity<Attendee>()
.HasOptional(a => a.TravelSchedule)
.WithRequired(r => r.Attendee);
modelBuilder.Entity<TravelSchedule>()
.HasRequired(m => m.ArrivalMode)
.WithMany(m => m.Arrivals)
.HasForeignKey(m => m.ArrivalModeId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<TravelSchedule>()
.HasRequired(m => m.DepartureMode)
.WithMany(m => m.Departures)
.HasForeignKey(m => m.DepartureModeId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Attendee>()
.HasOptional(a => a.LodgingArrangement)
.WithRequired(l => l.Attendee);
以下是我的种子方法的摘录。
var attendees = GetAttendeesFromCsv();
context.Attendees.AddOrUpdate(a => a.Email, attendees.ToArray());
context.SaveChanges();
var dbAttendees = context.Attendees.ToList();
foreach (var attendee in dbAttendees)
{
attendee.TravelSchedule = CreateTravelSchedule();
context.Entry<Attendee>(attendee).State = EntityState.Modified;
context.SaveChanges();
}
由于使用了CsvHelper包,GetAttendeesFromCsv()
将CSV中的记录提取到Attendee对象中。CreateTravelSchedule创建一个新的TravelSchedule实体,并使用extensionmethod.com中的SelectRandom()
方法用查找表中的数据填充它。最重要的是,我将CSV行提取到Attendee对象中,添加一个随机生成的新TravelSchedule实体,并将生成的Attendee与附件TravelSchedule一起保存。
但这不起作用相反,上面的代码将TravelSchedule记录添加到数据库中,但表中的AttendeeId始终设置为0。此外,与会者表中填充了CSV中的所有记录,但每行的TravelScheduleId也始终为0。但是,当与调试器参与者一起执行update-database
调用时。Id填充正确,因此根据我的理解,EF应该发现两者是相关的,并与与会者同时保留相关的TravelSchedule。那么EF为什么不把这两张唱片联系起来呢?
将循环更改为:
foreach (var attendee in dbAttendees)
{
var travel = CreateTravelSchedule();
travel.AttendeeId = attendee.Id; // I also tried just travel.Attendee = attendee, without success
context.TravelSchedules.Add(travel);
context.SaveChanges();
}
导致此错误的结果:
System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.TravelSchedules_dbo.Attendees_Id". The conflict occurred in database "MySite.DAL.MyContext", table "dbo.Attendees", column 'Id'.
因此,我似乎无法将TravelSchedule实体添加到与会者,和我也无法通过创建TravelSchedule然后附加与会者来"倒退"。
令人沮丧的是,我的控制器中的注册表逻辑运行良好,摘录如下。演练是,注册控制器使用静态WorkflowManager类在会话中存储每个屏幕的数据(视图模型),该类处理屏幕之间的持久性。用户确认后,控制器从WorkflowManager中提取每个屏幕的详细信息,通过AutoMapper运行这些信息,将其转换为相关的已填充DAL实体,将这些实体附加到与会者实体,并将其全部保存到数据库中。
同样,这非常好,可以毫无错误地保存与会者及其两个子实体。以下是控制器动作的相关摘录:
var attendeeRegistration = WorkflowManager.GetAttendeeRegistration();
var travelRegistration = WorkflowManager.GetTravelRegistration();
using (var db = new MyContext())
{
var attendee = Mapper.Map<Attendee>(attendeeRegistration);
attendee.AnotherChildEntity = db.ChildEntities.Find(attendeeRegistration.SelectedChildEntityId);
var travel = Mapper.Map<TravelSchedule>(travelRegistration);
travel.ArrivalMode = db.TravelModes.Find(travelRegistration.SelectedArrivalModeId);
travel.DepartureMode = db.TravelModes.Find(travelRegistration.SelectedDepartureModeId);
var lodging = Mapper.Map<LodgingArrangement>(lodgingRegistration);
lodging.LodgingLocation = db.LodgingLocations.Find(lodgingRegistration.SelectedLodgingLocationId);
attendee.Comments = comments;
attendee.TravelSchedule = travel;
attendee.LodgingArrangement = lodging;
db.Attendees.Add(attendee);
db.SaveChanges();
}
这非常有效。在用户确认注册完成之前,这些对象都不在数据库中。所以我不明白为什么我可以在这里将新实体持久化到数据库中,但我不能做上面Seed方法中看起来相同的事情。
非常感谢任何帮助或想法。这几天来一直给我带来无尽的悲痛。谢谢
Attendee
和TravelSchedule
之间的关联为1:1。EF通过在依赖的实体(此处:TravelSchedule
)中创建主键来实现1:1关联,该主键也是其主体主体(Attendee
)的外键。
因此CCD_ 11和CCD_ 12不用于关联,并且它们的值保持为0。这意味着:Seed
在没有这些字段/属性的情况下工作(我甚至希望它能与一起工作),它通过Id
字段建立关联。