我使用我的实体框架代码优先定义了以下类;
public class Parent: Entity<int>
{
public Guid Reference { get; set; } = Guid.NewGuid();
public int UserId { get; set; }
public virtual ICollection<Alert> Child1 { get; set; } = new HashSet<Child1>();
public virtual ICollection<History> Child2 { get; set; } = new HashSet<Child2>();
}
public class Child1: Entity<int>
{
public int ParentId { get; set; }
public Parent Parent { get; set; }
public DateTime Date { get; set; }
}
public class Child2 : Entity<int>
{
public int ParentId{ get; set; }
public Parent Parent { get; set; }
public string Title { get; set; }
}
我的DataContext
中引用了这些内容,如下所示;
public virtual DbSet<Parent> Parents{ get; set; }
public virtual DbSet<Child1> Child1 { get; set; }
public virtual DbSet<Child2> Child2 { get; set; }
通过重写OnModelCreating并添加,我已经禁用了Cascade Delete的所有内容;
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
此时,一切运作良好。我使用以下内容发布数据库并进行测试;
var parent = new Parent {UserId = 1};
parent.Child1.Add(new Child1 { Date = DateTime.Now});
parent.Child1.Add(new Child1 { Date = DateTime.Now});
parent.Child1.Add(new Child1 { Date = DateTime.Now});
parent.Child2.Add(new Child2 { Title = "Title" });
parent.Child2.Add(new Child2 { Title = "Title" });
parent.Child2.Add(new Child2 { Title = "Title" });
parent.Child2.Add(new Child2 { Title = "Title" });
context.Monitors.Add(monitor);
context.SaveChanges();
查看数据库,我可以看到记录,并且一切正确。
如果我然后将以下内容添加到相同的代码中;
context.Monitors.Remove(monitor);
context.SaveChanges();
我得到了一个错误,正如我所料;
操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。
此时我查看为这些特定类启用了Cascade Delete,将我项目中的所有其他类保留为显式删除。我将以下内容添加到我的datacontext中;
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child1)
.WithMany()
.WillCascadeOnDelete(true);
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child2)
.WithMany()
.WillCascadeOnDelete(true);
我重置了我的迁移,添加了初始迁移,删除并完全发布了数据库。
我可以看到以下内容添加到我的初始MIgration中;
.ForeignKey("Parent.Child1", t => t.Child1_Id, cascadeDelete: true)
.ForeignKey("Parent.Child2", t => t.Child2_Id, cascadeDelete: true)
现在,当我运行与上面相同的代码来添加记录时,我现在得到以下错误;
发生System.InvalidCastException HResult = 0x80004002
Message =无法转换类型的对象 “System.Collections.Generic.HashSet1[Child1]' to type 'Child1'.
1.AddToLocalCache(IEntityWrapper wrappedEntity,Boolean applyConstraints)at System.Data.Entity.Core.Objects.EntityEntry.TakeSnapshotOfSingleRelationship(RelatedEnd relatedEnd,NavigationProperty n,Object o)at System.Data.Entity.Core.Objects.EntityEntry.TakeSnapshotOfRelationships() 在 System.Data.Entity.Core.Objects.Internal.EntityWrapperWithoutRelationships
Source=EntityFramework StackTrace: at System.Data.Entity.Core.Objects.DataClasses.EntityReference1.TakeSnapshotOfRelationships(EntityEntry entry) at System.Data.Entity.Core.Objects.ObjectContext.AddSingleObject(EntitySet entitySet, IEntityWrapper wrappedEntity, String argumentName) at System.Data.Entity.Core.Objects.ObjectContext.AddObject(String entitySetName, Object entity) at System.Data.Entity.Internal.Linq.InternalSet
1&LT;&GT; c__DisplayClassd.b__c() at System.Data.Entity.Internal.Linq.InternalSet1.ActOnSet(Action action, EntityState newState, Object entity, String methodName) at System.Data.Entity.Internal.Linq.InternalSet
1.Add(Object entity)
在System.Data.Entity.DbSet`1.Add(TEntity实体)at Kinexus.Tools.TestApplication.Program.Main(String [] args)in d:\个人\ Kinexus_TFS \工具箱\ Kinexus.Tools \ Kinexus.Tools.TestApplication \的Program.cs:行 180
错误发生在这行代码中,但没有改变;
context.Monitors.Add(monitor);
我得到错误所说的内容,但我很难理解为什么以及如何解决。
答案 0 :(得分:2)
fluent配置与您的模型不匹配,这会导致EF假设其他关系,从而创建其他FK列。
为了使用流畅的API生成预期的关系映射,使用描述导航和/或FK属性的存在/不存在的正确重载是至关重要的。在您的情况下,无参数WithMany()
调用告诉EF该关系是单向(即没有集合导航属性),因此当EF发现未映射的集合导航时属性,它创建另一个单向关系,这次没有引用导航属性,默认FK列名称为EntityName_Id
。
同时查看您的映射,您完全颠倒了引用和集合属性配置。 Has/WithOptional
应在Has/WithMany
- 集合时指定引用。由于FK不可为空,因此您应使用Has/WithRequired
代替Has/WithOptional
。
话虽如此,解决问题只需应用正确的映射:
modelBuilder.Entity<Parent>()
.HasMany(p => p.Child1)
.WithRequired(c => c.Parent)
.HasForeignKey(c => c.ParentId)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Parent>()
.HasMany(p => p.Child2)
.WithRequired(c => c.Parent)
.HasForeignKey(c => c.ParentId)
.WillCascadeOnDelete(true);