使用实体框架代码保存分离的对象图首先导致主键冲突

时间:2013-11-20 14:15:18

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

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

但是,保存对象图后,我偶然发现了主键冲突异常。

对象图非常简单:

一个Issue可以包含多个WorkItems,每个AuthorUser)。

对象在外部填充(使用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; }
    }
}

Simplified WorkItem:

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表格看起来也很好,花花公子也可以在人们期望的列上使用FK

现在,当保存问题时,如果插入对象图将会很好,并且对现有对象的引用将被自动识别并且可选地仅插入或引用。

我尝试相应地添加问题:

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();
}

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

2 个答案:

答案 0 :(得分:1)

我终于做了类似的事情,非常费力,我仍然希望找到一个更好的方法。 找出实体是否已经附加或存在于数据库中,结果是过多地污染了模型(实​​现IEquatable<T>很好,但我认为在我的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();
        }
    }
}

答案 1 :(得分:0)

Issue对象中有一个小错误。

&#34;返回_workItems ??新名单();&#34;如果_workItems变为null,则可以在每次获取时返回一个新的WorkItem。这是固定版本。

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

    public String Id {
        get; set;
    }

    public List<WorkItem> WorkItems { get; private set; }
}