EF6代码优先,多个级联路径,以及奇怪的FK行为

本文关键字:行为 FK 级联 代码 EF6 路径 | 更新日期: 2023-09-27 18:08:06

我将尝试在这里只放置模型的相关部分,因为有相当多的类。希望这足以捕获问题:

public class Solve
{
    public int SolveID { get; set; }
    public int LocationID { get; set; }
    public virtual Location Location { get; set; }
    public int ProfileID { get; set; }
    public virtual Profile Profile { get; set; }
    public int BillID { get; set; }
    public virtual Bill Bill { get; set; }
    public int? PanelID { get; set; }
    public virtual Panel Panel { get; set; }
}
public class Location
{
    public int LocationID { get; set; }
    [Index]
    [StringLength(48)]
    public string Name  { get; set; }
    [Index]
    public State State { get; set; }
    public double Latitude  { get; set; }
    public double Longitude { get; set; }
    public virtual List<Profile> Profiles { get; set; }
}
public class Profile
{
    public int ProfileID { get; set; }
    public int LocationID { get; set; }
    public virtual Location Location { get; set; }
    public double Capacity { get; set; }
    public virtual List<ProfileSample> ProfileSamples { get; set; }
}
public class ProfileSample
{
    [Key, ForeignKey("Profile")]
    [Column(Order = 1)]
    public int ProfileID { get; set; }
    public virtual Profile Profile { get; set; }
    [Key]
    [Column(Order = 2)]
    [DataType(DataType.Date)]
    public DateTime Date { get; set; }
    [Key]
    [Column(Order = 3)]
    public TimeSpan TimeOfDay { get; set; }
    public double SampleValue { get; set; }
}

所以一切都工作得很好,直到我引入Solve类,这时它开始抱怨"多个级联路径"。我在上下文中添加了以下内容,从那时起似乎就没问题了:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Solve>()
        .HasRequired(s => s.Location)
        .WithRequiredDependent()
        .WillCascadeOnDelete(false);
}

除了它的行为不正常:

using (Model.BlueData bd = new Model.BlueData())
{
    Random rng = new Random();
    s = new Model.Solve()
    {
        Location = bd.Locations.Find(rng.Next(0, bd.Locations.Count())),
        Bill     = bd.Bills.Find(rng.Next(0, bd.Bills.Count())),
        Profile  = bd.Profiles.Find(rng.Next(0, bd.Profiles.Count()))
    };
    bd.Solves.Add(s);
    bd.SaveChanges();
    s = bd.Solves
        .Where(u => u.SolveID == s.SolveID)
        .Include(u => u.Location)
        .Include(u => u.Profile)
        .Include(u => u.Profile.ProfileSamples)
        .Include(u => u.Bill)
        .FirstOrDefault();
}

因此,上面的代码只是生成一个随机的Solve对象,将其添加到数据上下文中,然后再次检索它以及所有相关数据。当然有一种更优雅的方式来做这件事,但现在这只是测试代码,以确保我的应用程序的其他部分工作。

因此,正如预期的那样,当我创建Solve s对象时,s.Location是一个特定的位置,具有ID,例如1609,当然s.LocationIDs.SolveID都等于0

将其添加到数据上下文并保存更改后,s.SolveID等于位置的ID(在本例中为1609)。这很奇怪。我尝试在Solve类中添加[Key]属性到SolveID[ForeignKey("Location")]LocationID,但没有区别。

我试过各种各样的事情,比如从Solve中删除Profile,或者从位置中删除List<Profile> Profiles。我现在不记得了,但有一些事情确实可以纠正s.SolveID设置为位置的ID行为。

但是这些属性都是有原因的,如果可能的话,我不希望为了让这个工作而删除它们。我不明白为什么会发生这种情况,或者如何正确地纠正它。谢谢你的帮助。

EF6代码优先,多个级联路径,以及奇怪的FK行为

首先,LocationSolve对象中被引用,因此位置是主要的,求解是依赖的,我认为在这种情况下,这种映射是错误的-

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Solve>()
        .HasRequired(s => s.Location)
        .WithRequiredDependent()
        .WillCascadeOnDelete(false);
}

应该是-

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Solve>()
        .HasRequired(s => s.Location)
        .WillCascadeOnDelete(false);
}
其次,由于Solve引用外键,定义应该是-
public class Solve
{
    public int SolveID { get; set; }
    [ForeignKey("Location")]
    public int LocationID { get; set; }
    public virtual Location Location { get; set; }
    [ForeignKey("Profile")]
    public int ProfileID { get; set; }
    public virtual Profile Profile { get; set; }
    [ForeignKey("Bill")]
    public int BillID { get; set; }
    public virtual Bill Bill { get; set; }
    [ForeignKey("Panel")]
    public int? PanelID { get; set; }
    public virtual Panel Panel { get; set; }
}

第三,当保存对象时,您必须首先保存1)主要对象,否则EF将尝试创建新条目或2)您必须手动附加它们。在这些方法中,我发现最简单的是(1),在保存主端之后,我只分配外键,EF按预期工作。

using (Model.BlueData bd = new Model.BlueData())
{
    Random rng = new Random();
    s = new Model.Solve()
    {
        LocationID = bd.Locations.Find(rng.Next(0, bd.Locations.Count())).LocationID,
        BillID     = bd.Bills.Find(rng.Next(0, bd.Bills.Count())).BillID,
        ProfileID  = bd.Profiles.Find(rng.Next(0, bd.Profiles.Count())).ProfileID
    };
    s.Bill = s.Location = s.Profile = null; //otherwise EF tries to create them
    bd.Solves.Add(s);
    bd.SaveChanges();
    s = bd.Solves
        .Where(u => u.SolveID == s.SolveID)
        .Include(u => u.Location)
        .Include(u => u.Profile)
        .Include(u => u.Profile.ProfileSamples)
        .Include(u => u.Bill)
        .FirstOrDefault();
}

编辑1:location类为-

public class Location
{
    [Key]  //mark location ID as primary key
    public int LocationID { get; set; }
    [Index]
    [StringLength(48)]
    public string Name  { get; set; }
    [Index]
    public State State { get; set; }
    public double Latitude  { get; set; }
    public double Longitude { get; set; }
    public virtual List<Profile> Profiles { get; set; }
}