2

I have a problem with EF6 when using "disconnected" contexts. I'm using database first and have modelled a many-to-many relationship this way (there is a junction table behind the relation shown, with two FKs, which together make up the composite PK for the junction table. No other columns in that junction table):

EF database first edmx

Since I want to use EF in a disconnected way (short-lived contexts, ready for a web API) I have also implemented the "painting the state" method from Julie Lerman and Rowan Millers book on the DbContext. Specifically the method they describe in chapter 4 of the book, "Recording original values" (p.102 and forward). This is my ApplyChanges method:

public static void ApplyChangesInGraph<TEntity>(TEntity root) where TEntity : class, IObjectWithState
    {
        using (var context = new NovaEntities2())
        {
            context.Set<TEntity>().Add(root);
            foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
            {
                IObjectWithState stateInfo = entry.Entity;
                entry.State = ConvertState(stateInfo.State);
                if (stateInfo.State == State.Unchanged)
                {
                    ApplyPropertyChanges(entry.OriginalValues, stateInfo.OriginalValues);
                }
            }
            context.SaveChanges();
        }
    }

With this in place, I get an exception with the following test:

[Fact]
    public void ShouldNotResultInAnInsertOfPlaceOfEmployment()
    {
        ResetDbToKnownState();
        Employee employee;
        using (var context = new NovaEntities2())
        {
            employee = context.Employees
                //.Include(e => e.PlacesOfEmployment) // If enabled, an exception is thrown.
                .First();
        }
        employee.Name = "A new name";

        NovaEntities2.ApplyChangesInGraph(employee);
    }

If I enable the .Include above, the following exception occurs:

System.Data.Entity.Infrastructure.DbUpdateExceptionAn error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

InnerException: System.Data.SqlClient.SqlExceptionViolation of PRIMARY KEY constraint 'PK_EmployeePlaceOfEmployment'. Cannot insert duplicate key in object 'dbo.EmployeePlaceOfEmployment'. The duplicate key value is (140828, 14). Ie. with the .Include added above, EF issues an update of the Employee (fine) AND an insert of the already existing PlaceOfEmployment (not so fine) for the simple case above, where I only try to update the name of the Employee.

I cannot for the life of me figure out why an INSERT occurs here, violating the primary key. I've tried stepping through the foreach in the ApplyChanges method, and verified that all entity states are being set correctly. Indeed, on the first iteration, the Employee entity starts out as Added, and ends up in the Modified state. Next, the eager loaded PlaceOfEmployment entity is being processed and it starts out as Added and ends up in the Unchanged state. However, an INSERT is still being generated, resulting in the exception.

From SQL profiler:

The insert statement causing PK violation

4

1 回答 1

2

我不知道这种“绘制状态”方法,但是您的示例不起作用我并不感到惊讶。

发生context.Set<TEntity>().Add(root)三件事:

  • 根 (= employee) 处于Added状态
  • 所有孩子 (= employee.PlacesOfEmployment) 都处于Added状态
  • 根和子之间的所有关系条目都处于Added状态

当您迭代时,context.ChangeTracker.Entries您只从上下文中获取实体,而不是关系条目(它们是存储在上下文中的单独工件)。在这个循环中,你将 root 和 children 的状态分别重置为Modifiedor Unchanged。但这只是那个实体状态的变化。关系状态仍然存在,Added并且这些状态被转换为INSERT“关系表”。这就是为什么您得到重复键异常的原因,因为链接根和子项的记录已经存在。

尽管底层ObjectContext还允许访问实体状态条目之外的关系条目,但我怀疑它是否会有所帮助,因为如果每个实体ApplyChangesInGraph只有一个属性,该方法如何识别传递给该方法的图形已添加或删除或未更改的关系State?

context.Set<TEntity>().Attach(root);现在,如果您使用而不是,您可能可以使您的测试方法工作,...Add(root)...因为这会将所有实体和关系置于Unchanged状态(或者 - 我相信 - 它根本不会在上下文中存储任何关系条目)。因此,不会发生 INSERT 到链接表(也不会从该表中删除)。但是,我担心,如果您有一个具有真正新(或已删除)关系的对象图,而您实际上希望链接表有一个 INSERT(或 DELETE)关系,那么...AttachApplyChangesInGraph方法将无法正常工作。

老实说,我不知道如何实现该通用方法,以便它通常适用于所有可能的更改场景。在我看来,对象图(State每个实体一个属性)只是不包含足够的信息来描述在图断开连接时可能发生的所有关系变化。(Julie Lerman 和 Rowan Miller 提议如何实施它会让我感兴趣。)

于 2014-04-10T17:41:08.190 回答