问题在于 Id 的生成。最简单和最安全的方法是让新的 DbContext 生成 Id,就好像元素被添加为新对象一样
这意味着您必须从旧数据库中读取所有元素并将它们添加到新数据库中。
using (var oldDbContext = ...)
{
using (var newDbContext = ...)
{
var oldItems = oldDbContext.OldTable.ToList();
foreach (var oldItem in oldItems)
{
var newItem = converOldItem(newItem);
// you need your Id to have default value (zero)!
var addedNewItem = newDbcontext.NewTable.Add(newItem);
}
...
newDbContext.SaveChanges();
}
}
如果您有一对多的关系,您将需要 addedNewItem。例如,如果老师有很多学生,那么您的老师在SaveChanges 之前不会有TeacherId。因此,您不能将教师的 ID 分配给学生中的外键。您必须:
var newStudentToAdd = ConvertFromOld(oldStudent);
newSturdentToAdd.Teacher = addedTeacher;
这一切的缺点是内存中会有很多数据,转换过程会相当慢。优点:非常安全,并且在转换过程中您可以进行(默认)更改。
您可以通过禁用对目标数据库中更改的检测来提高速度,从而强制保存所有内容,而不检查数据是否更改。
因为你正在创建一个新的数据库,所有数据都被添加了,因此所有数据都应该被保存,因此你不需要更改检测:DbContextConfiguration.AutoDetectChangesEnabled Property
newDbContext.Configuration.AutoDetectChangesEnabled = false;
另一个更危险的方法是重用现有的 ID。如果您这样做,您必须自己为转换过程之后将添加的所有项目生成 id。确保以后生成ID与旧数据库中生成ID的方法兼容。
如果您决定自己生成 ID。覆盖`DbContext.SaveChanges1:
class NewDbContext : DbContext
{
...
public override int SaveChanges()
{
GenerateIds();
base.SaveChanges();
}
private void GenerateIds()
{
foreach (var addedEntry in this.ChangeTracker.Entries
.Where(entry => entry.State == EntityState.Added))
{
((IID)addedEntity).Id = idGenerator.CreateId();
}
}
}
为此,每个Entry(DbSet 中带有主键的每个类)都应该有一个指示主键的接口,如下所示:
public interface IID
{
int Id {get; set;}
}
public class Teacher : IID
{
public int Id {get; set;}
...
}
public class Student : IID
{
public int Id {get; set;}
...
}
IdGenerator 应该创建与旧数据库的 IdGenerator 相似的 ID。在转换过程中,您想使用旧数据库的 Id,因此请确保不会重新创建这些 Id