Я решил заменить / преобразовать DataTable в IDataReader из-за проблем с памятью.
По прошествии некоторого времени поисков Google и MSDN я наткнулся на это на http://www.michaelbowersox.com/2011/12/22/using-a-custom-idatareader-to-stream-data-into-a-database / и Bulk Вставьте Sql Server миллионы записей .
Поскольку я использую LumenWorks Fast CSV Reader, и я еще не понял, как сообщить CsvReader, что IDataReader использует две разные версии полей. :-( CsvReader.FieldCount является ключевым здесь, но я не вижу, как сообщить CsvReader использовать один из двух новых классов, имеющих интерфейс IDataReader. См. Исходный скрипт и измененный скрипт ниже ... Спасибо ...
// Оригинальные скрипты ...
var dbConnection = new SqlConnection(_dbConnectionString);
using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly))
{
while(csvReader.ReadNextRecord())
{
if (csvReader.FieldCount == 48)
{
//Version 1...
dataRow["DealerId"] = csvReader[0];
dataRow["DealerName"] = csvReader[1];
//Etc...
}
else if (csvReader.FieldCount == 51)
{
//Version 2...
dataRow["DealerId"] = csvReader[0];
dataRow["DealerName"] = csvReader[1];
//Etc...
}
else { throw new Exception("Field Total Count Mismatched"); }
dataTable.Rows.Add(dataRow);
}
dbConnection.Open();
dbBulkCopy.WriteToServer(dataTable);
}
}
// Новый скрипт ...
var dbConnection = new SqlConnection(_dbConnectionString);
using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
dbConnection.Open();
using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly))
{
csvReader.ReadNextRecord();
dbBulkCopy.WriteToServer(
if (csvReader.FieldCount == 48)
{
//Version 1...
csvReader....??? //Assign a custom class having IDataTable...
}
else if (csvReader.FieldCount == 51)
{
//Version 2...
csvReader....??? //Assign a custom class having IDataTable...
}
else { throw new Exception("Field Total Count Mismatched"); }
);
}
}
// Пример скрипта ...
using (var file = new StreamReader(path))
using (var csv = new CsvReader(file, true)) // true = has header row
using (var bcp = new SqlBulkCopy(connection)) {
bcp.DestinationTableName = "TableName";
bcp.WriteToServer(csv);
}
Поскольку это будет немного долго, я пишу это как ответ.
* Я предполагаю, что, несмотря на то, что у вас есть два вида csv-файла, поступающего с разными заказами на поле, целевая таблица одинакова . * [РЕДАКТИРОВАТЬ] Не нужно предполагать, вы указали в своем комментарии.
Чтобы иметь возможность понять ваш контекст, я получил некоторые примеры данных здесь.
Предположим, что первый тип файла выглядит примерно так:
Rk,Year,Age,Tm,Lg,Id,G,W,L,W-L,Finish,
1,1978,37,Atlanta Braves,NL,,162,69,93,.426,6
И второй тип типа (некоторые столбцы меняются на возраст <-> Готово и есть дополнительные поля)
Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3
1,1978,Very good year,Atlanta Braves,NL,,162,69,93,.426,96,,,,
Итак, целевая таблица будет выглядеть примерно как (только столбцы)
Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3
Я вижу два варианта (опция +1 в конце) здесь:
ОПЦИЯ 1
[Предположим, что Test4 и Test5 являются столбцами, которые существуют в целевой таблице, но отсутствуют в обоих файлах CSV]
Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3,Test4,Test5
Разбор всех файлов, которые у вас есть (оба типа), переписывает их на один (или несколько, который зависит от вас) в соответствии с форматом, который вы определили. Таким образом, у вас есть только один (или несколько) файл с уникальным форматом.
Вы можете проанализировать этот файл, чтобы вставить его в базу данных с помощью csv reader, поскольку проблема несовместимости полей обрабатывается с последним полученным вами файлом в уникальном формате.
ВАРИАНТ 2
Вы будете выполнять операцию SqlBulkCopy дважды. В первом раунде вы будете читать файлы с 48 полями, а в следующем раунде вы будете читать файлы, имеющие 51 поле.
var FilesWith48Fields = Directory.GetFiles(@"D:\Data\48Fields", "*.csv");
foreach (var fileName in FilesWith48Fields)
{
using (var file = new StreamReader(fileName))
using (var csv = new CsvReader(file, true)) // true = has header row
using (var bcp = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls))
{
bcp.DestinationTableName = "fletchsodTable";
// map the first field of your CSV to the column in Database
var mapping1 = new SqlBulkCopyColumnMapping(0, "FirstColumnName");
bcp.ColumnMappings.Add(mapping1);
var mapping2 = new SqlBulkCopyColumnMapping(1, "SecondColumnName");
bcp.ColumnMappings.Add(mapping2);
....
bcp.WriteToServer(csv);
}
}
И повторите то же самое с файлами, имеющими 51 поле
var FilesWith51Fields = Directory.GetFiles(@"D:\Data\51Fields", "*.csv");
......
Более подробную информацию о SqlBulkCopyColumnMapping можно найти здесь .
ВАРИАНТ 3
Если вы хотите приключений при создании своего Data Reader, вот несколько ссылок:
Пример внедрения на codeproject
ЛИЧНОЕ ЗАМЕЧАНИЕ Из-за нехватки времени, для аналогичной проблемы, я отказался от 3-го варианта, потому что со всеми модульными тестами и оптимизацией, которые вам нужно будет сделать, и с другими настройками, это может занять время ( по крайней мере, для меня это было случай )
ВАРИАНТ 4 Возможно, с отображением столбцов, указанным в OPTION 2, вы хотели бы реализовать свой путь, проверив количество полей. Но инстинктивно я предлагаю не считать поля строгими закодированными целыми числами.