EF6 Code First,多个级联路径和奇怪的FK行为

时间:2016-09-21 22:00:43

标签: c# entity-framework entity-framework-6 code-first ef-fluent-api

我将尝试在此处仅放置模型的相关部分,因为有很多类。希望它足以解决问题:

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.LocationID和{{ 1}}都等于s.SolveID

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

我尝试了从LocationID删除Solve或从位置删除Profile等各种内容。我现在还不记得了,但是有些事情确实可以纠正Solve设置到该位置的ID行为。

但是这些属性都是有原因的,如果可能的话,我宁愿不必删除它们只是为了让它工作。我不明白为什么会这样,或者如何正确地纠正它。我感谢任何协助。

1 个答案:

答案 0 :(得分:2)

首先在Location对象中引用Solve,所以位置是主体,求解是依赖的,我认为在这种情况下这种映射是错误的 -

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:位置类 -

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; }
}