简体   繁体   English

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

[英]Creating copy of entities with many to many relationship without duplicating one of the type

I have problem with copying entities with many to many relationship. 我在复制具有多对多关系的实体时遇到问题。 I have three entities Company , Role and User defined like this: 我有三个实体CompanyRoleUser定义如下:

Company: 公司:

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

    public string Name { get; set; }

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

User: 用户:

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

    public string Name { get; set; }

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

Role: 角色:

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

    public string Name { get; set; }

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

Also, I defined many to many relationship between users and roles: 另外,我定义了用户和角色之间的多对多关系:

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

I used migrations to create tables in db and obviously EF created table TUserRole (so far everything good). 我使用迁移在db中创建表,显然使用EF创建了表TUserRole(到目前为止一切正常)。

And now, I would like to create copy of company and users but without copying roles (so I want to create new records at the tables TCompany, TUser and TUserRole, but no new records at the TRole). 现在,我想创建公司和用户的副本,但不复制角色(因此,我想在表TCompany,TUser和TUserRole上创建新记录,但在TRole上没有新记录)。

I thought that something like this would work but I'm getting exception: 我以为这样的事情会奏效,但我遇到了例外:

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

And the exception is Saving or accepting changes failed because more than one entity of type 'Mackas.EF.Model.Role' have the same primary key value. 例外是保存或接受更改失败,因为多个“ Mackas.EF.Model.Role”类型的实体具有相同的主键值。

I understand why I'm getting this (because there is more than one role with the same ID), but I don't know what should be my approach. 我知道为什么要得到这个(因为有多个具有相同ID的角色),但是我不知道应该采用什么方法。

Any suggestions? 有什么建议么?

I'm using EF 6.1.3. 我正在使用EF 6.1.3。

Using AsNoTracking generally is a good idea to obtain a graph of entities that aren't attached to a context. 通常,使用AsNoTracking是获取未附加到上下文的实体图的好主意。 As you know, adding the root entity ( company ) to a new context will mark all entities in the graph as Added and copying entities is a piece of cake. 如您所知,将根实体( company )添加到新的上下文中会将图形中的所有实体标记为“已Added ,复制实体简直是小菜一碟。

But there's one bummer. 但是,有一个令人讨厌。 AsNoTracking causes EF to materialize a new object for each entity in the result set, because it has no way to track that an entity has already been materialized. AsNoTracking导致EF为结果集中的每个实体实例化一个新对象,因为它无法跟踪某个实体已经实例化。 So you're OK as long as the object graph only diverges off the root entity. 因此,只要对象图仅偏离根实体,就可以。 Ie as long as all associations are 1 - 0..n . 即,只要所有关联均为1 - 0..n It that is true, all entities in the graph will represent exactly one "real" entity. 没错,图中的所有实体都将恰好代表一个“真实”实体。

However, in your case, there's a m - n association between User and Roles . 但是,在您的情况下, UserRoles之间存在m - n关联。 The graph converges . 该图收敛 If some users have the same roles, EF will create duplicate Role objects when using AsNoTracking . 如果某些用户具有相同的角色,则在使用AsNoTracking时,EF将创建重复的Role对象。

[By the way, contrary to EF6, EF-core manages to create unique entities even with AsNoTracking ] [顺便说一句,与AsNoTracking相反,即使使用AsNoTracking ,EF-core也可以创建唯一的实体]

The way to go here is to query the object graph by one context, as POCOs, not proxies, and then add/attach it to a second context: 此处的方法是通过一个上下文(即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();
}

Proxies have a reference to the context they were created by, so you can't attach them to a second context. 代理引用了创建它们的上下文,因此您不能将它们附加到第二个上下文。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM