【问题标题】:Adding Item with Many-to-Many Relationship In Entity Framework在实体框架中添加具有多对多关系的项目
【发布时间】:2011-10-13 00:42:18
【问题描述】:

当我尝试添加具有多对多关系的项目时出现主键违规错误:

我有两个类 - 具有多对多关系的文章和标签:

public class Article
{
    public int ID { get; set; }
    public string Text { get; set; }
    public   ICollection<Tag>  Tags { get; set; }
}

public class Tag
{ 
    [Key]
    public string UrlSlug { get; set; }
    public string Name { get; set; }
    public ICollection<Article> Articles{ get; set; }
}

当我添加新文章时,我允许用户输入任何标签,然后如果标签尚未在数据库中创建,我想创建一个新标签,或者将标签添加到文章对象的标签集合中标签已经存在。

因此,当我创建新的 Article 对象时,我会调用以下函数:

public static Tag GetOrLoadTag(String tagStr)
{
    string tagUrl = Tag.CreateTagUrl(tagStr);
    var db = new SnippetContext();
    var tagFromDb = from tagdummy in db.Tags.Include(x => x.Articles)
                    where tagdummy.UrlSlug == tagUrl
                    select tagdummy;
    if (tagFromDb.FirstOrDefault() != null)
    { return tagFromDb.FirstOrDefault(); }
    else
    {
        //create and send back a new Tag
    }
}

此函数主要检查数据库中是否有可用的标签,如果有则返回该标签,然后使用 article.Tags.Add() 将其添加到 Article 对象的标签集合中。

但是,当我尝试使用下面的代码保存它时,我得到一个违反 PRIMARY KEY 约束的错误

 db.Entry(article).State = EntityState.Modified;
 db.SaveChanges();

我不知道应该如何在文章和现有标签之间建立关系。

【问题讨论】:

  • 能否提供更多细节和完整代码?

标签: c# .net entity-framework exception primary-key


【解决方案1】:

在整个操作过程中使用相同的上下文实例,你的生活会轻松很多:

using (var ctx = new MyContext())
{
    Article article = ctx.Articles.Single(a => a.Id == articleId);
    Tag tag = ctx.Tags.SingleOrDefault(t => t.UrlSlug == tagUrl);
    if (tag == null) 
    {
       tag = new Tag() { ... }
       ctx.Tags.AddObject(tag);
    }

    article.Tags.Add(tag);
    ctx.SaveChanges();
}

如果您不想从数据库加载文章(如果您知道该文章存在,则该查询是多余的)您可以使用:

using (var ctx = new MyContext())
{
    Article article = new Article() { Id = articleId };
    ctx.Articles.Attach(article);

    Tag tag = ctx.Tags.SingleOrDefalut(t => t.UrlSlug == tagUrl);
    if (tag == null) 
    {
       tag = new Tag() { ... }
       ctx.Tags.AddObject(tag);
    }

    article.Tags.Add(tag);
    ctx.SaveChanges();
}

【讨论】:

  • 谢谢你成功了。我猜很多人会因为使用不同的上下文而被实体框架绊倒。问题是在同一个操作中做所有事情有点违背我们通常如何用不同的操作对 DAL 进行编程。最好的选择似乎是将上下文作为 ref 参数传递。
  • @Judo 这就是为什么我问你“所以确保当你从代码中的 db 检索标签时,你使用的数据上下文与调用 SaveChanges 时使用的数据上下文相同”
  • 请注意,因为我在这里偶然发现 - 如果您要抽象存储库模式背后的上下文,请确保您可以正确通过注入共享上下文/工厂。但是,如果您跨方法/范围共享上下文,您可能会遇到失败的提交会破坏后续不相关的提交的问题——像这种“撤消”方法可能会有所帮助吗? rundevrun.blogspot.com/2012/06/…
  • @FernandoTorres 这意味着您不仅有一个 Tag 对象,还有一系列它们(例如,从 UI 的复选框列表中检索)。所以你只需要在循环中添加标签这样的操作。
【解决方案2】:

您如何创建新标签?以及如何将现有或创建的实体附加到文章中。

使用类似的东西

Article a = new Article(...);
a.tags.add(GetOrLoadTag("some tag"));

阅读这篇文章http://thedatafarm.com/blog/data-access/inserting-many-to-many-relationships-in-ef-with-or-without-a-join-entity/

【讨论】:

  • 如果标签存在,我只需使用 article.Tag.Add(existingTag) 添加它。但是当我尝试保存它时会引发主键违规错误。
  • 当它是一个现有标签时,您需要让框架知道它是一个现有实体。框架正在尝试再次插入实体。请确保您已在主键上设置了标识列。
  • 好的,那么我如何让框架知道它是一个现有的实体?
  • 设置主键字段为INT IDENTITY,看看是否有效?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-14
  • 2018-01-01
  • 2021-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多