创建具有多对多关系的实体副本,而不复制其中一种类型

时间:2017-01-26 17:30:30

标签: c# entity-framework many-to-many

我在复制具有多对多关系的实体时遇到问题。 我有三个实体公司角色用户定义如下:

公司

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

    public string Name { get; set; }

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

用户:

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

    public string Name { get; set; }

    public virtual IList<Role> Roles { get; set; }
}

作用:

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

    public string Name { get; set; }

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

此外,我定义了用户和角色之间的多对多关系:

public class UserConfiguration : EntityTypeConfiguration<User>
{
    public UserConfiguration()
    {
        ToTable("TUser");
        HasKey(x => x.Id);

        HasMany(x => x.Roles).WithMany(x => x.Users).Map(m =>
        {
            m.MapLeftKey("UserId");
            m.MapRightKey("RoleId");
            m.ToTable("TUserRole");
        });
    }
}

我使用迁移在db中创建表,显然EF创建了表TUserRole(到目前为止一切都很好)。

现在,我想创建公司和用户的副本,但没有复制角色(所以我想在TCompany,TUser和TUserRole表创建新记录,但在TRole没有新记录。)

我认为这样的事情会起作用,但我得到例外:

Context context = new Context();

var company = context.Companies.Include(x => x.Users.Select(u => u.Roles)).AsNoTracking().SingleOrDefault();

context.Companies.Add(company);

foreach (var user in company.Users)
{
    foreach (var role in user.Roles)
    {
        context.Entry(role).State = EntityState.Unchanged;
    }
}

context.SaveChanges();

例外是保存或接受更改失败,因为多个类型的实体&#39; Mackas.EF.Model.Role&#39;具有相同的主键值。

我理解为什么我会这样做(因为有多个角色具有相同的ID),但我不知道应该采用什么方法。

有什么建议吗?

我使用的是EF 6.1.3。

1 个答案:

答案 0 :(得分:2)

使用AsNoTracking通常最好获取未附加到上下文的实体图表。如您所知,将根实体(company)添加到新上下文会将图中的所有实体标记为Added,复制实体就是小菜一碟。

但是有一个让人失望。 AsNoTracking导致EF为结果集中的每个实体实现一个新对象,因为它无法跟踪实体是否已实现。只要对象图仅偏离根实体,您就可以了。即只要所有关联都是1 - 0..n。这是真的,图中的所有实体都将代表一个真实的&#34;真实的&#34;实体。

但是,在您的情况下,m - nUser之间存在Roles关联。图表收敛。如果某些用户具有相同的角色,则在使用Role时,EF会创建重复的AsNoTracking个对象。

[顺便说一下,与EF6相反,EF-core设法创建独特的实体,即使使用AsNoTracking]

这里的方法是通过一个上下文查询对象图,作为POCO,而不是代理,然后将其添加/附加到第二个上下文:

Company company;
using (Context context = new Context())
{
    context.Configuration.ProxyCreationEnabled = false;
    company = context.Companies.Include(x => x.Users.Select(u => u.Roles))
                     .SingleOrDefault();
}

using (Context context = new Context())
{
    context.Companies.Add(company);

    foreach (var user in company.Users.SelectMany(u => u.Roles)
                                .Distinct())
    {
        context.Entry(role).State = EntityState.Unchanged;
    }

    context.SaveChanges();
}

代理可以引用它们创建的上下文,因此您无法将它们附加到第二个上下文。