为什么我的Seed()方法无法正确保存和连接实体?

时间:2015-11-19 21:19:12

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

我正在为我的组织建立一个会议注册网站,有多位贵宾和嘉宾发言人。要求是跟踪每个与会者的许多细节,包括他们的到达和离开计划以及他们当地的住宿信息。为了便于与利益相关者讨论我们需要构建的报告类型,我想用CSV中的一批记录填充我的开发数据库,​​其中包含随机生成的信息,如姓名,到达/离开日期/时间等。这将允许我们查看工作现场,而无需多次注册和重新注册。

但是,我只是不能让Seed方法正确保存相关记录,但处理注册的控制器工作正常。

我的数据库结构基本上是一个参与者实体,其中包含TravelScheduleLodgingArrangement和各种查找的子实体。以下是我的实体的摘录:

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实体,并使用附加的TravelSchedule保存生成的参与者。

除非这不起作用。相反,上面的代码将TravelSchedule记录添加到数据库中,但AttendeeId始终在表中设置为0。此外,Attendees表中还填充了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();
}

这完美无缺。在用户确认注册完成之后,这些对象都不在数据库中。所以我不明白为什么我可以在这里将新实体持久化到数据库中,但我不能在我看来在上面的种子方法中做同样的事情。

非常感谢任何帮助或想法。这几天来我一直在悲伤。感谢。

1 个答案:

答案 0 :(得分:1)

AttendeeTravelSchedule之间的关联是1:1。 EF通过在依赖实体(此处为:TravelSchedule)中创建主键来实现1:1关联,该实体也是主体的外键实体(Attendee)。

因此Attendee.TravelScheduleIdTravelSchedule.AttendeeId不会用于关联,其值仍为0.这意味着:Seed无法使用这些字段/属性(我甚至可以期望它与一起工作,它通过Id字段建立关联。