Прежде чем это станет отмеченным как дубликат, я видел много таких ответов, как этот Convert IEnumerable to DataTable, и попытался сделать что-то подобное в способе создания метода расширения. Я задаю свой вопрос, поскольку проблема, которая может возникнуть, может лежать где-то в другом месте.Эффективный способ конвертировать из IEnumerable <T> в DataTable
По существу у меня довольно большие IEnumerable (около 16 - 17 пунктов мила) до этого момента я не действительно имел никаких проблем с этим, пока я не попытался преобразования к DataTable с помощью метода расширения:
/// <summary>
/// Converts IEnumberable to datatable. Mainly for use when using SQLBulkCopy/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <param name="customColumnOrder">Custom order for columns allows me to make sure that the order of columns will always be the same. Am open for suggestions for better ways to do this</param>
/// <returns></returns>
public static DataTable ToDataTable<T>(this IEnumerable<T> collection, List<Tuple<string, int, int>> customColumnOrder)
{
DataTable dt = new DataTable();
var type = collection.First().GetType();
foreach (var column in customColumnOrder)
{
dt.Columns.Add(column.Item1, Nullable.GetUnderlyingType(type.GetProperty(column.Item1).PropertyType) ?? type.GetProperty(column.Item1).PropertyType);
}
//Populate the table
foreach (T item in collection)
{
DataRow dr = dt.NewRow();
dr.BeginEdit();
foreach (var column in customColumnOrder)
{
dr[column.Item1] = type.GetProperty(column.Item1).GetValue(item) ?? DBNull.Value;
}
dr.EndEdit();
dt.Rows.Add(dr);
}
return dt;
}
Это отлично подходит для небольших столов размером около 100 000 предметов, но начинает действительно бороться, когда он попадает в миллионы. Я просто продолжаю получать тайм-ауты. Есть ли более эффективный/вообще лучший способ сделать преобразование из IEnumerable в datatable? Я конвертирую в DataTable, поэтому я могу использовать SQLBulkCopy для получения данных в DataBase.
EDIT 0: Heres где данные пропускают через из
/// <summary>
/// SqlBulkCopy for saving large amounts of data
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataToSave"></param>
/// <param name="modelManager">Custom manager to use alongside the model</param>
/// <param name="conn">Connection string to DB</param>
public void BatchSave<T>(IEnumerable<T> dataToSave, IData modelManager, string conn)
{
var model = dataToSave.First();
using (SqlConnection sqlconn= new SqlConnection(conn))
{
sqlconn.Open();
using (SqlCommand cmd = new SqlCommand(GetCreateScript(modelManager, model), sqlconn))
{
//Create temp table to do initial insert into
cmd.ExecuteNonQuery();
SqlBulkCopy copy = new SqlBulkCopy(cmd.Connection);
copy.DestinationTableName = "#tempTableForImport";
//Convert data to DataTable
DataTable dt = dataToSave.ToDataTable(modelManager.GetDataColumnsOrder());
//Copy to temp table
copy.WriteToServer(dt);
}
using (SqlCommand cmd = new SqlCommand(modelManager.GetInsertSproc(), sqlconn) { CommandType=CommandType.StoredProcedure })
{
//Clean up data and move to final table
cmd.ExecuteNonQuery();
}
sqlconn.Close();
}
}
EDIT 1: Недавно внесены изменения кода с использованием высказанное, его теперь используют Fastmember:
public void BatchSave<T>(IEnumerable<T> dataToSave, IData modelManager, string conn)
{
var model = dataToSave.First();
using (SqlConnection sqlconn = new SqlConnection(conn))
{
sqlconn.Open();
using (var bcp = new SqlBulkCopy(sqlconn))
{
using (var reader = ObjectReader.Create(dataToSave, modelManager.GetDataColumnsOrder().Select(s => s.Item1).ToArray() /*modelManager.GetDataColumnsOrder().Select(obj=>obj.Item1).ToString()*/))
{
using (SqlCommand cmd= new SqlCommand(GetCreateScript(modelManager, model), sqlconn))
{
cmd.ExecuteNonQuery();
bcp.DestinationTableName = "#tempTableForImport";
bcp.WriteToServer(reader);
}
using (SqlCommand cmd = new SqlCommand(modelManager.GetInsertSproc(), sqlconn) { CommandType = CommandType.StoredProcedure })
{
cmd.ExecuteNonQuery();
}
}
}
sqlconn.Close();
}
}
Это имеет однако я все еще получаю «Тайм-аут истек» в этой строке bcp.WriteToServer(reader);
. Спасибо всем за помощь до 30 секунд, больше идей по этому поводу? Может быть, каким-то образом увеличить продолжительность до таймаута?
не использовать foreach на IEnumerable коллекция. –
@ M.kazemAkhgary спасибо за ответ, можете ли вы объяснить, почему? – Srb1313711
@ M.kazemAggary почему нет? Что бы вы предложили вместо этого? –