使用实体框架代码保存分离对象图,首先导致主键违规 [英] Save detached object graph using Entity Framework code first causes Primary Key violation

查看:146
本文介绍了使用实体框架代码保存分离对象图,首先导致主键违规的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



在保存对象图之后,我绊倒了,我试图保存一个POCO的对象图,我已经使用Code First流畅的符号映射到了EF6。主要关键违规例外。



对象图非常简单:



一个问题可以包含多个 WorkItems ,每个作者(作为用户



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



当我尝试保存一个涉及同一作者的两个工作项的问题时,我会期待插入该问题,插入工作项,插入一个作者,另一个被引用或被更新。 / p>

然而,会发生什么问题,插入问题,插入工作项,并插入对同一用户的引用,导致主键违例。



简化问题对象:

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

public string Id {get;组; }

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

简化的WorkItem:

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

public string AuthorLogin
{
get;组;
}

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

简化用户对象:

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

他们的代码优先配置:

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

HasRequired(p => p.Author)
.WithMany(b => b.WorkItems)
.HasForeignKey(x => x.AuthorLogin);
}
内部用户配置()
{
HasKey(x => x.Login);
}

所有相当直截了当在数据库创建之后,表格看起来很好,并且dandy也是,有一个人会期待他们的列的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();
}

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

解决方案

我终于做了类似这样的事情相当费力,我还是想找个更好的方法。
找出一个实体是否已经附加或存在于数据库中,原来是模型太多(实现 IEquatable< T> 是好的,但我认为在我的POCO上执行 IEntityWithKey 会污染POCO太多(直到上文中似乎不足以追踪实体)

  internal static void保存(此列表<问题>问题)
{
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
{
//更新现有实体的属性
existing.Url = workItem.Author.Url;

//替换参考
workItem.Author = existing;
}
db.SaveChanges();
}

var existingWorkItem = db.WorkItems.SingleOrDefault(e => e.Id == workItem.Id);
if(existingWorkItem == null)
{
db.WorkItems.Add(workItem);
}
else
{
//更新现有实体的属性
existingWorkItem.Duration = workItem.Duration;

//替换参考
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
{
//更新现有实体的属性
existingIssue.SpentTime = issue.SpentTime;
}

db.SaveChanges();
}
}
}


I'm trying to save an object graph of POCOs I have mapped to EF6 using Code First fluent notations.

Upon saving the object graph however, I stumble upon primary key violation exceptions.

The object graph is quite simple:

One Issue can contain multiple WorkItems with each one Author (as User).

The objects are populated externally (using a Web API)

When I attempt to save an issue with two workitems which refer to the same author, I would expect the issue to be inserted, the workitems to be inserted and one author to be inserted, and the other one to be referenced or be updated.

What happens however is that the issue is inserted, the workitems are inserted and both references to the same user are inserted, resulting in a primary key violation.

Simplified Issue object:

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

Simplified user object:

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

Their Code-first configurations:

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

All quite straightforward. Upon database create, de tables look fine and dandy too, with FKs on the columns where one would expect them

Now when saving the issue, it would have been nice if the object graph would be inserted, and the reference to existing objects would be recognized automagically and optionally inserted or referenced only.

I attempt to add issues accordingly:

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

Is the solution to this issue that I walk through the object graph to manually add or attach the other objects in the graph too? I would expect by defining the proper Foreign Key values these references would be recognized.

解决方案

I finally ended up doing something similar to this, quite laborious and I would still like to find a better way. Finding out whether an entity is already attached or exists in the database turned out to be pollute the model too much (implementing IEquatable<T> is fine, but I think implementing IEntityWithKey on my POCOs pollutes the POCO too much. (and till that did not seem to suffice tracking entities in the context)

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

这篇关于使用实体框架代码保存分离对象图,首先导致主键违规的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆