Entity Framework Code First多对多关系创建重复行

8
我的问题是由于存在两个上下文。我修改了代码,只保留一个上下文,问题就解决了。
我的用户(User)有一个用户联系人(UserContact)列表,其中每个联系人都有一个联系选项(ContactOption)。这是一个简单的1对多、多对1模型,中间有一个UserContact表。
如果我从数据库中提取用户并创建新的UserContact,但将ContactOption设置为现有项目(从数据库中提取),当我保存更改时,实体框架会在数据库中创建一个新的ContactOption,它基本上是我添加到UserContact的重复项(除了它获得一个新的id)。
我已经花费数小时来解决这个问题,但无法找到解决方法。有什么想法吗?
我使用存储库模式进行数据库查询,但我确保它们共享同一个上下文。
我使用以下代码从数据库中提取用户:
var user = _context.Users.Include("UserContacts.ContactOption")
              .Where(id => id == 1);

联系选项是通过以下方式提取的:

var co = _context.ContactOptions.FirstOrDefault(c => c.Id == id);

我将ContactOption添加到UserContact中,如下所示:

var contactOption = _context.ContactOptions.FirstOrDefault(c => c.Id == someId);

var contact = new UserContact { ContactOption = contactOption  };
contact.Data = "someData";

user.UserContacts.Add(contact);

我的模型长这样:

public class User
{
    [Key]
    public int Id { get; set; }

    public virtual ICollection<UserContact> UserContacts { get; set; }
}

public class UserContact
{
    [Key]
    public int Id { get; set; }

    [Required]
    public User User { get; set; }

    [Required]
    public ContactOption ContactOption { get; set; }
    public string Data { get; set; }
}

public class ContactOption
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}

1
你能展示一下将ContactOption与UserContact关联的代码吗? - Justin Soliz
Justin,我已按要求更新了问题。谢谢! - Dan Morphis
2个回答

2

我运行了你的代码并获得了完全符合预期的结果:在 UserContacts 表中插入新行,其中包含现有的 UserId 和 ContactOptionId。所以我不确定发生了什么问题,但是你可以尝试在 UserContact 对象中明确设置外键,这样你就可以完全控制 Code First 如何为你插入记录。为此,你需要按照以下方式更改 UserContact:

public class UserContact
{
    public int Id { get; set; }

    // By Convention these 2 properties will be picked up as the FKs: 
    public int UserId { get; set; }
    public int ContactOptionId { get; set; }

    [Required]
    public User User { get; set; }        
    [Required]
    public ContactOption ContactOption { get; set; }

    public string Data { get; set; }
}

然后你可以像这样修改你的代码:

var contactOption = _context.ContactOptions.Find(someId);
var user = _context.Users.Find(1);

var contact = new UserContact 
{ 
    ContactOptionId = contactOption.Id,  
    UserId = user.Id,
    Data = "someData"
};

user.UserContacts.Add(contact);
context.SaveChanges();

我不知道是否将你的答案标记为正确。它确实帮助我找到了我需要去的地方,但我没有像你那样解决它。我的问题最终是因为在我的ModelBinder中我创建了一个新的repository,导致有两个上下文。我通过一些依赖注入的好处来解决它,现在我的问题已经解决了! - Dan Morphis
感谢您的更新,是的,我知道问题出在其他地方,因为就像我说的那样,我只是复制粘贴了您的代码,它按预期工作。我发表这个答案的意图是向您展示如何放弃显式外键,这样我们就不再受任何未知的代码优先约定的限制,而且我们可以确保代码将按照我们想要的方式工作。 - Morteza Manavi
我会将您的答案标记为正确,因为它确实帮助了我。我还会编辑我的问题,指出创建两个上下文是我的问题所在。 - Dan Morphis

1

我的问题是由于有两个上下文引起的。我调整了一下代码,只保留一个上下文,问题就解决了。感谢 Morteza Manavi 指导我找到正确的方向。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接