EF6 Code First、多个级联路径和奇怪的 FK 行为
EF6 Code First, multiple cascade paths, and strange FK behaviour
我将尝试只将模型的相关部分放在这里,因为有很多 classes。希望这足以解决问题:
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
class 之前一切正常,此时它开始抱怨 "multiple cascade paths"。我在上下文中添加了以下内容,从那时起似乎就可以了:
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
和s.SolveID
都等于 0
.
将其添加到数据上下文并保存更改后,s.SolveID
等于位置的 ID(在本例中为 1609
)。这很奇怪。我尝试在 Solve
class 中将 [Key]
属性添加到 SolveID
并将 [ForeignKey("Location")]
添加到 LocationID
,但没有任何区别。
我试过各种方法,例如从 Solve
中删除 Profile
,或从 Location 中删除 List<Profile> Profiles
。我现在记不太清楚了,但确实有几件事可以更正 s.SolveID
设置为位置的 ID 行为。
但这些属性都是有原因的,如果可能的话,我宁愿不必为了让它工作而删除它们。我不明白为什么会这样,或者如何正确纠正它。感谢任何帮助。
首先 Location
在 Solve
对象中被引用,所以 location 是主要的,解决是依赖的,我认为在那种情况下这个映射是错误的 -
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:class 的位置 -
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; }
}
我将尝试只将模型的相关部分放在这里,因为有很多 classes。希望这足以解决问题:
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
class 之前一切正常,此时它开始抱怨 "multiple cascade paths"。我在上下文中添加了以下内容,从那时起似乎就可以了:
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
和s.SolveID
都等于 0
.
将其添加到数据上下文并保存更改后,s.SolveID
等于位置的 ID(在本例中为 1609
)。这很奇怪。我尝试在 Solve
class 中将 [Key]
属性添加到 SolveID
并将 [ForeignKey("Location")]
添加到 LocationID
,但没有任何区别。
我试过各种方法,例如从 Solve
中删除 Profile
,或从 Location 中删除 List<Profile> Profiles
。我现在记不太清楚了,但确实有几件事可以更正 s.SolveID
设置为位置的 ID 行为。
但这些属性都是有原因的,如果可能的话,我宁愿不必为了让它工作而删除它们。我不明白为什么会这样,或者如何正确纠正它。感谢任何帮助。
首先 Location
在 Solve
对象中被引用,所以 location 是主要的,解决是依赖的,我认为在那种情况下这个映射是错误的 -
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:class 的位置 -
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; }
}