C# 实体框架代码第一个现有数据库集EntityState。在多对多上未更改

C# 实体框架代码第一个现有数据库集EntityState。在多对多上未更改,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,我有一个现有的数据库,我先用EntityFramework6代码编码。我有一个用于选择、插入和删除的多对多关系。我对EF在现有关系的多对多表中添加额外插入有问题 模式: DbContext: protected override void OnModelCreating(DbModelBuilder modelBuilder) { Database.SetInitializer<MainContext>(null); modelBuil

我有一个现有的数据库,我先用EntityFramework6代码编码。我有一个用于选择、插入和删除的多对多关系。我对EF在现有关系的多对多表中添加额外插入有问题

模式:

DbContext:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
       Database.SetInitializer<MainContext>(null);

       modelBuilder.Entity<DocumentType>()
                .HasMany(u => u.DocumentStatuses)
                .WithMany()
                .Map(m =>
                {
                    m.MapLeftKey("DOCUMENT_TYPE_ID");
                    m.MapRightKey("DOCUMENT_STATUS_ID");
                    m.ToTable("DOCUMENT_TYPES_DOCUMENT_STATUSES");
                });

       base.OnModelCreating(modelBuilder);
    } 
包含以下内容的循环:

    if (item.Id != null)
     db.Entry(item).State = EntityState.Unchanged;
阻止添加新记录,但EF仍尝试将现有多对多记录的副本插入到“文档类型”“文档状态”中

单元测试:

        [TestMethod()]
        public void UpdateTest()
        {
            DocumentTypeRepository documentTypeRepository = new DocumentTypeRepository();
            DocumentType type = NewDocumentType(true);
            DocumentType typefromDb;
            string updatedBy = "DocumentTypeRepositoryTests.UpdateTest";
            bool actual;

            type.IsActive = true;
            type.TypeName = RandomValues.RandomString(18);
            type.DocumentStatuses.Add(DocumentStatusRepositoryTests.NewDocumentStatus(true));

            actual = documentTypeRepository.Update(type, updatedBy);
            Assert.AreEqual(true, actual);

            typefromDb = documentTypeRepository.GetById((int)type.Id);
            Assert.AreEqual(type.DocumentStatuses.Count, typefromDb.DocumentStatuses.Count);
        }
如何将多对多表设置为EntityState。当它已经存在时,将保持不变?

尝试替换

db.DocumentTypes.Add((DocumentType)entity);
db.Entry(entity).State = EntityState.Modified;


当对分离的实体执行CRUD操作时,不使用DbSet操作而只操作实体状态条目更容易。至少,它会减少错误。

这里需要注意两件事:

  • 向上下文添加
    实体时,该实体拥有的整个对象图将标记为
    已添加

  • 在任何关联中,这也将关联标记为新关联。但是,在1:n或1:1关联中,当其中一个端点的状态发生变化时,关联的状态也会发生变化,而在多对多关联中,关联的一部分是隐藏的,并且始终保持添加状态。(在引擎盖下,多对多关联是一个
    1:n:1
    关联,
    n
    部分在代码中不可见)

  • 所以这句话

    db.DocumentTypes.Add(entity);
    
    使关联获得添加的状态,以及随后的

    db.Entry(entity).State = EntityState.Modified;
    
    不会再改变这个了

    您必须确保现有的
    文档类型
    的状态从未更改为
    已添加
    ,您可以通过检查其
    Id
    值来执行此操作,这与检查
    文档状态
    的操作相同

    此外,您还必须更改设置
    文档状态的方式:

    foreach (var item in entity.DocumentStatuses)
    {
        db.Entry(item).State = item.Id != null
                                   ? EntityState.Unchanged
                                   : EntityState.Added;
    }
    

    我确实在上面有一行使用检查id的代码:if(entity.id==null)抛出新的InvalidOperationException(“DocumentTypeId在更新时不能为null”);我将其移除以保持示例更干净。有趣的是,我在您的示例中忽略了错误,但它没有保存添加到列表中的任何新状态,只是保留了旧状态。关键部分是删除。AddI在上面添加了我的单元测试,当我向列表添加新状态项并进行更新(使用新代码)时,它不会将新项添加到多对多表中。所以最后一个断言失败了,因为它只有第一个状态,而不是两个状态。@Josh Frank是对的,但其中涉及的内容比看上去的要多。我在回答中解释了这一点。一句话没用,这是有道理的。顺便说一句,强制转换是一个复制粘贴挂起,我有其他使用接口的对象,所以这就是为什么我有一个强制转换。所以问题在于Frank的更改不再试图向表插入重复的1:n:1关系。问题是,如果我将另一个DocumentStatus添加到DocumentType.DocumentStatus,然后调用此更新方法,它不会为新添加的DocumentStatus创建1:n:1记录。所以我们解决了一个问题,但又创造了一个新的问题。我也这么想并尝试了。但是如果你看一下我上面的单元测试,我并没有添加一个新的状态,而是添加了一个新的1:n:1与现有状态的关系。因此,项的EntityState状态将有一个Id,因此它将始终为EntityState。未更改,这是有意义的,因为它已经存在。我看不出其中到底发生了什么,但至少这是方向。它在我正在测试的简单多对多模式下工作。如果您从列表中删除一个项目并保存,我们会遇到相同类型的问题,它基本上不会删除1:n:1关系。您必须在
    DocumentType
    及其状态附加到上下文后删除一个项目,否则,更改跟踪器将永远不会知道曾经有一个项目要删除。
    db.DocumentTypes.Add(entity);
    
    db.Entry(entity).State = EntityState.Modified;
    
    foreach (var item in entity.DocumentStatuses)
    {
        db.Entry(item).State = item.Id != null
                                   ? EntityState.Unchanged
                                   : EntityState.Added;
    }