【问题标题】:Save detached object graph using Entity Framework code first causes Primary Key violation使用实体框架代码保存分离的对象图首先会导致主键违规
【发布时间】:2013-11-20 14:15:18
【问题描述】:

我正在尝试使用 Code First 流畅符号保存我已映射到 EF6 的 POCO 的对象图。

然而,在保存对象图时,我偶然发现了主键违规异常。

对象图很简单:

一个Issue 可以包含多个WorkItems,每个Author(如User)。

对象在外部填充(使用 Web API)

当我尝试使用两个引用同一作者的工作项保存问题时,我希望插入该问题,插入工作项并插入一位作者,另一位被引用或被更新了。

然而发生的情况是问题被插入,工作项被插入并且对同一用户的两个引用被插入,导致主键冲突。

简化的问题对象:

public class Issue
{
    public Issue()
    {
        WorkItems = new List<WorkItem>();
    }

    public string Id { get; set; }

    private List<WorkItem> _workItems;
    public List<WorkItem> WorkItems
    {
        get { return _workItems ?? new List<WorkItem>(); }
        set { _workItems = value; }
    }
}

简化的工作项:

public class WorkItem
{
    public string Id { get; set; }

    public string AuthorLogin
    {
        get; set;
    }

    private WorkItemAuthor _author;
    public WorkItemAuthor Author
    {
        get { return _author; }
        set { _author = value;
            if (value != null)
            {
                AuthorLogin = value.Login;
            }
            else
            {
                AuthorLogin = string.Empty;
            }
        }
    }
}

简化的用户对象:

public class User
{
    public string Login { get; set; }
    public string FullName { get; set; }
}

他们的代码优先配置:

    internal IssueConfiguration()
    {
        HasKey(x => x.Id);
        HasMany(x => x.WorkItems);
    }
    internal WorkItemConfiguration()
    {
        HasKey(x => x.Id);

        HasRequired(p => p.Author)
            .WithMany(b => b.WorkItems)
            .HasForeignKey(x=>x.AuthorLogin);
    }
    internal UsersConfiguration()
    {
        HasKey(x => x.Login);
    }

一切都很简单。在创建数据库时,de 表看起来也很漂亮,而且在人们期望它们的列上有 FKs

现在保存问题时,最好插入对象图,并且可以自动识别对现有对象的引用,并且可以选择仅插入或引用。

我尝试相应地添加问题:

using (var db = new Cache.Context())
{
    if (db.Issues.Any(e => e.Id == issue.Id))
    {
        db.Issues.Attach(issue);
        db.Entry(issue).State = EntityState.Modified;
    }
    else
    {
        db.Issues.Add(issue);
    }
    db.SaveChanges();
}

这个问题的解决方案是我遍历对象图以手动添加或附加图中的其他对象吗?我希望通过定义正确的外键值来识别这些引用。

【问题讨论】:

    标签: c# entity-framework ef-code-first primary-key entity-framework-6


    【解决方案1】:

    我终于做了类似的事情,相当费力,我仍然想找到更好的方法。 找出一个实体是否已经附加或存在于数据库中被证明对模型的污染过多(实现IEquatable&lt;T&gt; 很好,但我认为在我的 POCO 上实现IEntityWithKey 会过多地污染 POCO。(直到那个在上下文中似乎不足以跟踪实体)

    internal static void Save(this List<Issue> issues)
    {
        using (var db = new Context())
        {
            foreach (var issue in issues.ToList())
            {
    
                foreach (var workItem in issue.WorkItems.ToList())
                {
                    if (workItem.Author != null)
                    {
                        var existing = db.Users.SingleOrDefault(e => e.Login == workItem.Author.Login);
                        if (existing == null)
                        {
                            db.Users.Add(workItem.Author);
                        }
                        else
                        {
                            //Update existing entities' properties
                            existing.Url = workItem.Author.Url;
    
                            //Replace reference
                            workItem.Author = existing;
                        }
                        db.SaveChanges();
                    }
    
                    var existingWorkItem = db.WorkItems.SingleOrDefault(e => e.Id == workItem.Id);
                    if (existingWorkItem == null)
                    {
                        db.WorkItems.Add(workItem);
                    }
                    else
                    {
                        //Update existing entities' properties
                        existingWorkItem.Duration = workItem.Duration;
    
                        //Replace reference
                        issue.WorkItems.Remove(workItem);
                        issue.WorkItems.Add(existingWorkItem);
                    }
    
                    db.SaveChanges();
                }
    
    
                var existingIssue = db.Issues.SingleOrDefault(x => x.Id == issue.Id);
                if (existingIssue == null)
                {
                    db.Issues.Add(issue);
                }
                else
                {
                    //Update existing entities' properties
                    existingIssue.SpentTime = issue.SpentTime;
                }
    
                db.SaveChanges();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      Issue 对象中有一个小错误。

      "return _workItems ?? new List();"如果 _workItems 曾经为空,则可以在每次获取时返回一个新的 WorkItem。这是固定版本。

      public class Issue {
          public Issue() {
              WorkItems = new List<WorkItem>();
          }
      
          public String Id {
              get; set;
          }
      
          public List<WorkItem> WorkItems { get; private set; }
      }
      

      【讨论】:

      • 如果我没记错的话,私人设置器会禁止从 EF 设置它。另一种解决方法是:get { return _workItems ?? (_workItems = new List&lt;WorkItem&gt;());"}
      • 啊哈,同意。然后还要删除构造函数中的 new 。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-07
      • 2015-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多