我有一种情况,我想创建一条与现有记录相同的新记录,包括其所有 1:1 和 1:many 关系,但更改 1 列除外。
那么,我可以以某种方式读取一个对象,为每个属性调用
Include()
,然后将 Id
属性设置为 0,更改 1 列,然后 Add()
到 DbSet
并保存它吗?并复制所有包含的属性吗?
我尝试了两种不同的方法,但出现了异常。但在我看来,从逻辑上讲,这应该可行。
进行深层复制的一种简单方法是 Automapper,但是您需要重新绑定任何引用、引用现有行而不是克隆为新行的相关实体。
例如,如果我们有一个包含 B 类集合的 A 类,以及对 C 类的引用:我们想要 A 类的新克隆,其中包含一组新的 B 类,但引用相同的 C 类实例:想想订单、订单项目和订单状态。新订单具有新的订单项目集合,但有引用,但不拥有状态。
var config = new Mapper.MappingConfiguration(cfg =>
{
cfg.CreateMap<ClassA, ClassA>()
.ForMember(src => src.C, opt => opt.Ignore());
cfg.CreateMap<ClassB, ClassB>();
};
var mapper = config.CreateMapper();
var classA = _context.ClassAs
.Include(a => a.Bs)
.Include(a => a.C)
.Single(a => a.Id == aId);
var newA = mapper.Map<ClassA>(classA);
newA.C = classA.C; // Reference the same C.
// Update any values in newA and/or newA.Bs...
_context.As.Add(newA);
_context.SaveChanges();
我们将映射器配置为忽略任何引用的实体并克隆我们想要新副本的任何相关实体。然后我们可以编辑这些副本并将它们添加到上下文中。只要 Automapper 知道需要 ClassA 和 ClassB,当它遍历目标类型时,它就会搜索并构造新实例,并复制值。这些实体的 PK 是否设置并不重要,如果您具有检查新行的默认 PK 的逻辑,则可以将映射器配置为忽略它们。事实上,这些实体不会被跟踪,这意味着 EF 会将它们视为新实体。这就是为什么我们需要重新引用相关实体(ClassC)而不是克隆它。我们不希望 EF 插入新副本,而只是将新记录与现有跟踪引用相关联。