复制具有层次结构的模型

我的模型看起来像这样:

Company -Locations Locations -Stores Stores -Products 

所以我想制作公司的副本,并且还应该将其所有关联复制并保存到数据库中。

如果我将公司加载到内存中,我该怎么做?

 Company company = DbContext.Companies.Find(123); 

如果它很棘手,我可以遍历每个关联,然后调用创建一个新对象。 Id会有所不同,但其他一切都应该是一样的。

我使用的是EF 6。

使用EF克隆对象图是一块蛋糕:

 var company = DbContext.Companies.AsNoTracking() .Include(c => c.Locations .Select(l => l.Stores .Select(s => s.Products))) .Where(c => c.Id == 123) .FirstOrDefault(); DbContext.Companies.Add(company); DbContext.SaveChanges(); 

这里有几点需要注意。

  • AsNoTracking()至关重要,因为您不应该跟踪添加到上下文中的对象。
  • 现在,如果您Add() company ,其对象图中的所有实体也将被标记为已Added
  • 我假设数据库生成新的主键值(标识列)。 如果是这样,EF将忽略数据库中现有对象的当前值。 如果没有,您将必须遍历对象图并自己分配新值。

一个警告:只有当关联为1:0..n时,这才有效。 如果存在:m关联,则可以多次插入相同的实体。 例如,如果Store-Product为n:m且product A出现在store 1store 2 ,则product A将被插入两次。 如果你想防止这种情况,你应该通过一个上下文获取对象, 跟踪 (即没有AsNoTracking ),并在新的上下文中Add()它们。 通过启用跟踪,EF可以跟踪相同的实体,并且不会复制它们。 在这种情况下,应禁用代理创建,否则实体会保留对它们来自的上下文的引用。

更多细节:将相同的数据库合并为一个

我会为每个需要以这种方式克隆的模型添加一个方法,我也建议使用它的接口。

可以这样做:

 //Company.cs Company DeepClone() { Company clone = new Company(); clone.Name = this.name; //...more properties (be careful when copying reference types) clone.Locations = new List(this.Locations.Select(l => l.DeepClone())); return clone; } 

你应该为每个需要可复制的类和“子”类重复这个基本模式。 这样每个对象都知道如何创建自己的深层克隆,并将子对象的责任传递给子类,整齐地封装所有内容。

它可以这样使用:

 Company copyOfCompany123 = DbContext.Companies.Find(123).DeepClone; 

如果上述代码中有任何错误,我很抱歉; 我目前没有可用的Visual Studio来validation所有内容,我正在通过内存工作。


另一种使用序列化深度克隆对象的非常简单且代码有效的方法可以在这篇文章中找到。 如何在.Net(C#专门)中深层复制对象?

 public static T DeepClone(T obj) { using (var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); ms.Position = 0; return (T) formatter.Deserialize(ms); } } 

请注意,根据您的对象结构,这可能会有一些非常严重的资源和性能问题。 您要使用它的每个类也必须使用[Serializable]属性进行标记。