C# 实体框架创建不需要的行时出现间歇性问题

C# 实体框架创建不需要的行时出现间歇性问题,c#,sql-server,asp.net-mvc,entity-framework,C#,Sql Server,Asp.net Mvc,Entity Framework,我有一个使用EntityFrameworkV6的MVC应用程序。我们有一节课 public class ChildObject { public string Name { get; set; } .... } 映射到数据库中的表的。此表有6行从未更改。也不会有任何补充。我们有第二个类,定义如下: public class ParentClass { public int ChildObjectId { get; set; } public ChildObjec

我有一个使用EntityFrameworkV6的MVC应用程序。我们有一节课

public class ChildObject
{
    public string Name { get; set; }
    ....
}
映射到数据库中的表的。此表有6行从未更改。也不会有任何补充。我们有第二个类,定义如下:

public class ParentClass
{
    public int ChildObjectId { get; set; }
    public ChildObject ChildObject { get; set; }
    ....
}

每当创建或更新ParentClass对象时,逻辑仅引用ChildObjectId属性。只有在回拉数据以供查看时,才会引用ChildObject属性。但是,大约每月一次,ChildObject表中会出现一个与现有行重复的额外行。这显然会引起一些问题。然而,我看不出这是怎么发生的,因为我们只使用Id值进行保存。如果您能想到这种情况是如何发生的,我们将不胜感激。

您所描述的行为的典型罪魁祸首是,一个新的子实体是基于现有数据组成的,并附加到父实体,而不是与上下文相关联的引用。例如,可以将子对象作为一个集合加载以从中进行选择,然后将数据发送到视图。用户希望将现有子引用更改为6个选项之一。对服务器的回调传递了一个子对象模型,其中包含如下代码:

parent.ChildObject = new ChildObject{ Name = model.Name, ... } 
而不是:

var child = context.Children.Single(x => x.Id = model.ChildObjectId);
parent.ChildObject = child;
根据域的设置方式,在设置导航属性时,可能会遇到EF上下文创建新子实体的情况。使用FindSages检查ChildObject属性并查找setter的任何用法

通常,应避免将FK属性(ChildObjectId)与导航属性(ChildObject)结合使用,因为在导航引用中设置的内容与FK之间可能会出现混淆行为。实体应该用一个或另一个来定义。(但此时,如果使用导航属性,EF Core需要这两个属性。)

您的例子中的几个名人: 将导航属性标记为虚拟-这可确保EF分配并识别代理

选项A-删除FK子ID属性。对于父级,请使用EntityTypeConfiguration或初始化DbContext以映射FK列:

EntityTypeConfiguration:

public class ParentClassConfiguration : EntityTypeConfiguration<ParentClass>
{
  public ParentClassConfiguration()
  {
    ToTable("ParentTable");
    HasKey(x => x.ParentObjectId)
      .Property(x => x.ParentObjectId)
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

    HasRequired(x => x.ChildObject)
      .WithMany()
      .Map(x => x.MapKey("ChildObjectId"));
      .WillCascadeOnDelete(false);
  }
}
公共类ParentClassConfiguration:EntityTypeConfiguration { 公共ParentClassConfiguration() { ToTable(“ParentTable”); HasKey(x=>x.ParentObjectId) .Property(x=>x.ParentObjectId) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasRequired(x=>x.ChildObject) .有很多 .Map(x=>x.MapKey(“ChildObjectId”); .WillCascadeOnDelete(假); } } 或在生成上下文模型时:(在DbContext中)

模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Entity().HasRequired(x=>x.ChildObject).WithMany().Map(x=>x.MapKey(“ChildObjectId”)).WillCascadeOnDelete(false);
}
或选项B-确保FK与参考链接,并采取措施确保两者始终保持同步:

public class ParentClassConfiguration : EntityTypeConfiguration<ParentClass>
{
  public ParentClassConfiguration()
  {
    ToTable("ParentTable");
    HasKey(x => x.ParentObjectId)
      .Property(x => x.ParentObjectId)
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

    HasRequired(x => x.ChildObject)
      .WithMany()
      .HasForeignKey(x => x.ChildObjectId));
      .WillCascadeOnDelete(false);
  }
}
公共类ParentClassConfiguration:EntityTypeConfiguration { 公共ParentClassConfiguration() { ToTable(“ParentTable”); HasKey(x=>x.ParentObjectId) .Property(x=>x.ParentObjectId) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasRequired(x=>x.ChildObject) .有很多 .HasForeignKey(x=>x.ChildObjectId)); .WillCascadeOnDelete(假); } } 或在生成上下文模型时:(在DbContext中)

模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Entity().HasRequired(x=>x.ChildObject).WithMany().HasForeignKey(x=>x.ChildObjectId)).WillCascadeOnDelete(false);
}

据我所知,选项B是EF Core目前唯一可用的选项,它可能有助于缓解您的问题,但您仍需注意避免导航属性和FK之间的差异。我绝对推荐方案A,不过,如果您的代码经常访问FK列,则可能需要进行一些更改。

您是否对“ChildObject=”进行了解决方案范围的搜索,因为听起来您可能有一个位置,其中有人正在将对象分配给该属性,并且分配的对象的Id可能为0。是的,确实进行了站点范围的搜索我也在想什么
public class ParentClassConfiguration : EntityTypeConfiguration<ParentClass>
{
  public ParentClassConfiguration()
  {
    ToTable("ParentTable");
    HasKey(x => x.ParentObjectId)
      .Property(x => x.ParentObjectId)
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

    HasRequired(x => x.ChildObject)
      .WithMany()
      .HasForeignKey(x => x.ChildObjectId));
      .WillCascadeOnDelete(false);
  }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<ParentObject>().HasRequired(x => x.ChildObject).WithMany().HasForeignKey(x => x.ChildObjectId)).WillCascadeOnDelete(false);
}