IdentityUser的实体框架代理导致保存时出现PK违规

时间:2014-02-06 13:56:32

标签: entity-framework primary-key dbcontext asp.net-identity

我正在使用Entity Framework 6,我正在编写Save方法来更新现有“案例”的详细信息,如下所示:

public void Save(User user, Case @case)
{
    if (@case.Id == 0)
    {
        // deal with new case scenario
    }
    else
    {
        var existing = GetCase(@case.Id);

        // update existing's properties with those in @case

        context.Entity(existing).State = EntityState.Modified;
        context.Entry(existing.SubEntity1).CurrentValues.SetValues(@case.SubEntity1);
        context.Entry(existing.SubEntity2).CurrentValues.SetValues(@case.SubEntity2);
    }

    context.SaveChanges();
}

Case类型具有RaisedBy类型的User属性,该属性派生自ASP.NET Identity IdentityUser

进行GetCase调用后,我使用IQueryable.Include同时加载附加到RaisedBy媒体资源的用户。它似乎加载了User作为代理,一个监视,而调试显示类型为System.Data.Entity.DynamicProxies.User_后跟一个看起来像哈希的非常长的值。

当调用context.SaveChanges()时,EF似乎尝试将用户重新写入表格,从而生成带有消息的DbUpdateException

  

违反PRIMARY KEY约束'PK_dbo.IdentityUsers'。无法在对象'dbo.IdentityUsers'中插入重复键。重复键值为(0799b4c2-a38e-430c-8031-1c27878f2f43)。   声明已经终止。

据我所知,RaisedBy用户与从上下文加载的用户完全相同,所以我很难看到问题是如何悄然进入的。

1 个答案:

答案 0 :(得分:0)

鉴于我在问题中省略的代码,在评论后面

// update existing's properties with those in @case

没有人能回答我并不感到惊讶。

问题在于,在那个省略的代码块中:

existing.Events.Add(new Event
{
    CaseId = @case.Id,
    Author = user,
    Text = existing.GetChangeDescription(@case),
    DateTime = now,
    EventType = EventType.Update
});

我正在建立与User对象的关联,Entity Framework随后将其状态设置为添加,这是公平的,因为在此阶段没有在此上下文中加载此用户的跟踪,即使有,我会得到一个不同的“无法跟踪具有相同键的相同类型的对象”错误。最好的方法是:

  • 使用DbSet.Find从数据库加载用户,将传入的用户设置为此加载的用户
  • 不要设置导航属性,将其保留为null并在Event对象上设置AuthorId
  • 做一些聪明的事情,查询上下文以查看用户是否已经加载,如果没有将传递的用户附加到具有State = EntityState.Unchanged的上下文(类似于第一个选项,只是避免数据库命中)

我选择了1,因为它最适合我目前的架构,但是我正在考虑重构以使选项3成为可能。