Entity Framework - Code First 保存多对多关系

4

我有两个类:

public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<User> Users { get; set; }
}

public class User
{
    public int Id { get; set; }
    public string Email { get; set; }

    public virtual ICollection<Company> Companies { get; set; }
}

在我的MVC应用程序中,控制器从post中获取新的公司。我想要像这样将当前用户添加到创建的公司中。

User user = GetCurrentLoggedUser();
//company.Users = new ICollection<User>(); // Users is null :/
company.Users.Add(user);  // NullReferenceException
companyRepository.InsertOrUpdate(company);
companyRepository.Save();

正常工作的样子应该是什么样的?我还不清楚,但是在将用户添加到集合后,我预计会遇到保存到数据库的问题。如果您有任何关于它应该是什么样子的提示,将不胜感激。

2个回答

6
使用以下方法:
public class Company
{
    public int Id { get; set; }
    public string Name { get; set;}

    private ICollection<User> _users;
    public ICollection<User> Users
    {
        get
        {
            return _users ?? (_users = new HashSet<User>());
        }
        set
        {
            _users = value;
        }
    }
}

HashSet如果你在实体中也重写了EqualsGetHashCode,那么比其他集合更好。它会为你处理重复项。同时,懒惰的集合初始化更好。我记不清了,但我想我在我的第一个EF测试应用程序中初始化集合时遇到了一些问题,并且还使用了动态代理进行懒加载和更改跟踪。

有两种类型的实体:分离和附加。附加实体已经由上下文跟踪。通常,您可以从linq-to-entities查询或通过在DbSet上调用Create来获取附加实体。分离实体没有被上下文跟踪,但是一旦您调用AttachAdd将此实体附加,所有相关实体都将被附加/添加。当使用分离实体时,您必须处理的唯一问题是相关实体是否已存在于数据库中,而您只想创建新关系。

您必须了解的主要规则是Add方法和Attach方法之间的区别:

  • Add将以Added的形式附加图中的所有分离实体=>所有相关实体都将作为新实体插入。
  • Attach将以Unchanged的形式附加图中的所有分离实体=>您必须手动说明已修改了什么。

您可以使用以下方法手动设置任何附加实体的状态:

context.Entry<TEntity>(entity).State = EntityState....;

当使用分离式多对多关系时,通常需要使用这些技术来仅构建关系而不是将重复实体插入数据库。
根据我的经验,使用分离式实体图非常困难,特别是在删除关系后。因此,我总是从数据库加载实体图,并手动合并更改到已连接的图中,这些图能够完全跟踪所有更改。
请注意,您不能混合来自不同上下文的实体。如果要将实体从一个上下文附加到另一个上下文,则必须首先明确地从第一个上下文中分离实体。我希望您可以通过在第一个上下文中设置其状态为“Detached”来完成此操作。

此刻我看不出HashSet和Collection之间有太大的区别(我选择了HashSet)。您可以帮助我理解如何管理我的上下文。在获取用户之前,我将CompanyRepository的上下文设置为UserRepository。这给了我一个快速可行的解决方案。非常感谢您。 - bizon

2
在 Company 实体的构造函数中,您可以在 Users 属性上创建一个空集合。
public class Company
{
    public Company() {
      Users = new Collection<User>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<User> Users { get; set; }
}

就保存到数据库而言,我几天前曾询问过一个相关问题,并得到保证Entity Framework能够跟踪对相关实体所做的更改。在这里阅读相关信息:

当添加到父级时,子实体是否自动跟踪?


谢谢您的答复。当我添加从不同存储库(和不同上下文)加载的用户时,会出现错误:“实体对象不能由多个IEntityChangeTracker实例引用。”按照您问题中的更改方式进行更改后,一切都正常了,但它在数据库中创建了另一个用户,而不是使用给定的ID。User user = new UserRepository().GetByEmail(User.Identity.Name); company.Users.Add(new User { Id = user.Id, FirstName = user.FirstName, LastName = user.LastName, Email = user.Email}); companyRepository.InsertOrUpdate(company); companyRepository.Save(); - bizon

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