我在复制具有多对多关系的实体时遇到问题。 我有三个实体公司,角色和用户定义如下:
公司
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。
答案 0 :(得分:2)
使用AsNoTracking
通常最好获取未附加到上下文的实体图表。如您所知,将根实体(company
)添加到新上下文会将图中的所有实体标记为Added
,复制实体就是小菜一碟。
但是有一个让人失望。 AsNoTracking
导致EF为结果集中的每个实体实现一个新对象,因为它无法跟踪实体是否已实现。只要对象图仅偏离根实体,您就可以了。即只要所有关联都是1 - 0..n
。这是真的,图中的所有实体都将代表一个真实的&#34;真实的&#34;实体。
但是,在您的情况下,m - n
和User
之间存在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();
}
代理可以引用它们创建的上下文,因此您无法将它们附加到第二个上下文。