我得到这个错误时,我GetById()在一个实体,然后设置子实体的集合到我的新列表,来自MVC视图。

操作失败 关系是无法改变的 因为一个或多个外键 Properties是非空的。当一个 关系发生了变化 相关外键属性设置为 空值。如果外键是 不支持空值,新建 关系必须被定义 必须分配外键属性 另一个非空值或 必须删除不相关的对象。

我不太理解这句话:

这种关系无法改变 因为一个或多个外键 Properties是非空的。

我为什么要改变两个实体之间的关系?它应该在整个应用程序的生命周期内保持不变。

发生异常的代码只是简单地将集合中修改过的子类分配给现有的父类。这将有望满足取消子类,增加新的和修改。我本以为实体框架处理这个。

代码行可以提炼为:

var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();

当前回答

我使用了Mosh的解决方案,但我不清楚如何在代码中正确地实现组合键。

这就是解决方案:

public class Holiday
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int HolidayId { get; set; }
    [Key, Column(Order = 1), ForeignKey("Location")]
    public LocationEnum LocationId { get; set; }

    public virtual Location Location { get; set; }

    public DateTime Date { get; set; }
    public string Name { get; set; }
}

其他回答

这是因为子实体被标记为Modified而不是Deleted。

当执行parent. remove (Child)时,EF对子实体所做的修改只是将其父实体的引用设置为null。

当异常发生时,在执行SaveChanges()后,你可以通过在Visual Studio的即时窗口中输入以下代码来检查子对象的EntityState:

_context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified).ElementAt(X).Entity

其中X应替换为删除的实体。

如果你不能访问ObjectContext来执行_context.ChildEntity.Remove(child),你可以通过使外键成为子表中主键的一部分来解决这个问题。

Parent
 ________________
| PK    IdParent |
|       Name     |
|________________|

Child
 ________________
| PK    IdChild  |
| PK,FK IdParent |
|       Name     |
|________________|

这样,如果你执行parent.Remove(child), EF将正确地将实体标记为已删除。

出现这个问题是因为我们试图删除父表,但仍然存在子表数据。 我们利用级联删除来解决这个问题。

在模型中创建dbcontext类中的方法。

 modelBuilder.Entity<Job>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                .WithRequired(C => C.Job)
                .HasForeignKey(C => C.JobId).WillCascadeOnDelete(true);
            modelBuilder.Entity<Sport>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                  .WithRequired(C => C.Sport)
                  .HasForeignKey(C => C.SportId).WillCascadeOnDelete(true);

之后,在我们的API调用中

var JobList = Context.Job                       
          .Include(x => x.JobSportsMappings)                                     .ToList();
Context.Job.RemoveRange(JobList);
Context.SaveChanges();

级联删除选项删除父表以及与父相关的子表,使用这个简单的代码。用这个简单的方法试试。

删除范围用于删除数据库中的记录列表 谢谢

我尝试过这些方法和其他方法,但没有一个很有效。由于这是谷歌上的第一个答案,我将在这里添加我的解。

对我来说很有效的方法是在提交期间将关系排除在外,这样EF就不会搞砸了。我通过在DBContext中重新找到父对象并删除它来做到这一点。因为重新找到的对象的导航属性都是空的,所以在提交过程中会忽略子对象的关系。

var toDelete = db.Parents.Find(parentObject.ID);
db.Parents.Remove(toDelete);
db.SaveChanges();

注意,这里假设外键设置为ON DELETE CASCADE,所以当父行被删除时,子行将被数据库清除。

如果你正在使用自动绘图器,面临以下问题是一个很好的解决方案,它为我工作

https://www.codeproject.com/Articles/576393/Solutionplusto-aplus-Theplusoperationplusfailed

因为问题是我们正在映射空导航属性,我们实际上不需要在实体上更新它们,因为它们在契约上没有改变,我们需要在映射定义上忽略它们:

ForMember(dest => dest.RefundType, opt => opt.Ignore())

所以我的代码是这样的:

Mapper.CreateMap<MyDataContract, MyEntity>
ForMember(dest => dest.NavigationProperty1, opt => opt.Ignore())
ForMember(dest => dest.NavigationProperty2, opt => opt.Ignore())
.IgnoreAllNonExisting();

我不知道为什么其他两个答案这么受欢迎!

我相信您认为ORM框架应该处理它是正确的——毕竟,这是它承诺交付的。否则,您的域模型就会被持久性问题所破坏。如果你正确地设置了级联设置,NHibernate就能很好地管理它。在实体框架中也有可能,他们只是希望你在建立数据库模型时遵循更好的标准,特别是当他们不得不推断应该做什么级联时:

您必须使用“识别关系”来正确地定义父-子关系。

如果你这样做,实体框架知道子对象是由父对象标识的,因此它必须是一个“级联删除孤儿”的情况。

除了上面的,你可能需要(从NHibernate的经验)

thisParent.ChildItems.Clear();
thisParent.ChildItems.AddRange(modifiedParent.ChildItems);

而不是完全替换列表。

更新

@Slauma的评论提醒我,分离实体是整体问题的另一部分。为了解决这个问题,您可以采用使用自定义模型绑定器的方法,通过尝试从上下文加载模型来构造模型。这篇博客文章展示了我的意思。