Здесь есть некий крайний случай.Мне было поручено перенести все данные из одной базы данных в другую, где база данных назначения имеет другую схему.
Я решил написать утилиту WinForms для отображения и передачи данных при необходимости с помощью Entity Framework / ADO.NET.
До сих пор это прекрасно работало, за исключением одной конкретной таблицы с 2,5 миллионами записей.Передача занимает около 10 минут, когда я игнорирую все внешние ключи, однако, когда я начинаю отображать внешние ключи с помощью FirstOrDefault()
вызовов в списках данных, которые уже были перемещены в целевую базу данных, буквально 4 дня добавляются кколичество времени, которое требуется.
Мне нужно много раз запустить этот инструмент в ближайшие дни, так что это не совсем приемлемо для меня.
Вот мой текущий подход (Не мой первый подход, эторезультат большого количества проб и ошибок ради эффективности):
private OldModelContext _oldModelContext { get; } //instantiated in controller
using (var newModelContext = new NewModelContext())
{
//Takes no time at all to load these into memory, collections are small, 3 - 20 records each
var alreadyMigratedTable1 = newModelContext.alreadyMigratedTable1.ToList();
var alreadyMigratedTable2 = newModelContext.alreadyMigratedTable2.ToList();
var alreadyMigratedTable3 = newModelContext.alreadyMigratedTable3.ToList();
var alreadyMigratedTable4 = newModelContext.alreadyMigratedTable4.ToList();
var alreadyMigratedTable5 = newModelContext.alreadyMigratedTable5.ToList();
var oldDatasetInMemory = _oldModelContext.MasterData.AsNoTracking().ToList();//2.5 Million records, takes about 6 minutes
var table = new DataTable("MasterData");
table.Columns.Add("Column1");
table.Columns.Add("Column2");
table.Columns.Add("Column3");
table.Columns.Add("ForeignKeyColumn1");
table.Columns.Add("ForeignKeyColumn2");
table.Columns.Add("ForeignKeyColumn3");
table.Columns.Add("ForeignKeyColumn4");
table.Columns.Add("ForeignKeyColumn5");
foreach(var masterData in oldDatasetInMemory){
DataRow row = table.NewRow();
//With just these properties mapped, this takes about 2 minutes for all 2.5 Million
row["Column1"] = masterData.Property1;
row["Column2"] = masterData.Property2;
row["Column3"] = masterData.Property3;
//With this mapping, we add about 4 days to the overall process.
row["ForeignKeyColumn1"] = alreadyMigratedTable1.FirstOrDefault(s => s.uniquePropertyOnNewDataset == masterData.uniquePropertyOnOldDataset);
row["ForeignKeyColumn2"] = alreadyMigratedTable2.FirstOrDefault(s => s.uniquePropertyOnNewDataset == masterData.uniquePropertyOnOldDataset);
row["ForeignKeyColumn3"] = alreadyMigratedTable3.FirstOrDefault(s => s.uniquePropertyOnNewDataset == masterData.uniquePropertyOnOldDataset);
row["ForeignKeyColumn4"] = alreadyMigratedTable4.FirstOrDefault(s => s.uniquePropertyOnNewDataset == masterData.uniquePropertyOnOldDataset);
row["ForeignKeyColumn5"] = alreadyMigratedTable5.FirstOrDefault(s => s.uniquePropertyOnNewDataset == masterData.uniquePropertyOnOldDataset);
table.Rows.Add(row);
}
//Save table with SQLBulkCopy is very fast, takes about a minute and a half.
}
}
Примечание: uniquePropertyOn(New/Old)Dataset
чаще всего является уникальной строкой описания, разделяемой между наборами данных, не может соответствовать идентификаторам, поскольку они не будутто же самое для баз данных.
Я пытался:
- Вместо использования foreach, приведенного с помощью оператора linq
select
, улучшения не было. - Использовать
.Where(predicate).FirstOrDefault()
, значительного улучшения не наблюдалось - Запуск
FirstOrDefault()
для iqueryable вместо списков перенесенных данных, улучшения не наблюдалось. - Отображение в список вместодата, но это не имеет никакого значения в скорости отображения, а также делает объемные сохранения медленнее.
Я возился с идеей превратить foreach
в параллельный цикл foreach и заблокировать вызовы для данных, но я продолжаю сталкиваться с
Закрытие соединения с Entity Framework
при запросе списков в памяти при использовании параллельного foreach ... не совсем уверен, о чем идет речь, но изначально результаты скорости были многообещающими.
Я был бы рад опубликовать этот код / ошибки, если кто-то считает, что это правильный путь, но я не уверен больше ..