// Method GetExcelData opens 1 excel file, reads data row by row and adds
// it into the array of source Data Values (sourceDataValues in our case).
private void GetExcelData(string fullPath, ArrayList arrForValues)
{
Excel.Application excelapp = new Excel.Application();
excelapp.Visible = false;
// to avoid appearing of Excel window on the screen
Excel.Workbook excelappworkbook = excelapp.Workbooks.Open(
fullPath,
Type.Missing, Type.Missing, true, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
Excel.Worksheet excelworksheet = (Excel.Worksheet)excelappworkbook.Worksheets.get_Item(1);
Excel.Range excelcells = excelworksheet.UsedRange;
uint rowsNum = 0;
for (rowsNum = 1; rowsNum != excelcells.Rows.Count; rowsNum++)
{
arrForValues.Add((excelcells.Cells[rowsNum, 1] as Excel.Range).Value2);
}
excelappworkbook.Close(false, Type.Missing, Type.Missing);
excelapp.Quit();
}
Низкая производительность при чтении данных из листа Excel в ArrayList. C#
УСЛОВИЯ:
Нужно прочесть данные из Excel-файла и запихнуть их в ArrayList. В рабочей книги только один рабочий лист. Данные представляет собой вещественные числа. Все они расположены в одном столбце.
ПРОБЛЕМА:
Слишком много времени уходит на данный процесс - примерно 2 минуты чтоб наполнить ArrayList 10000 элементов.
Прошу вашей помощи в ускорении этого процесса. Ясно, что чтение данных в моей реализации неэффективно. Прошу вашего совета по повышению производительности. Желательно кусочек кода. Заранее спасибо.
Код:
Код:
private static IList<double> ReadValues(string path)
{
var application = new Application();
try
{
var workbook = (Workbook)ComHelper.Invoke(application.Workbooks, "_Open", path);
try
{
var sheet = (Worksheet)workbook.Worksheets[1];
var range = sheet.UsedRange;
var result = new List<double>(range.Count);
foreach (Range value in range)
result.Add((double) value.Value2);
return result;
}
finally
{
ComHelper.Invoke(workbook, "Close");
}
}
finally
{
application.Quit();
ComHelper.Release(application);
}
}
static class ComHelper
{
public static object Invoke<T>(T obj, string method_name, params object[] args)
{
var type = typeof(T);
const BindingFlags binding_flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
var method = type.GetMethod(method_name, binding_flags);
if (method == null)
{
method = obj.GetType().GetMethod(method_name, binding_flags);
if (method == null)
throw new ArgumentException(string.Format("Method {0} not found in type {1}", method_name, type.Name));
}
var parameters_count = method.GetParameters().Length;
var provided_args_count = args.Length;
if (provided_args_count > parameters_count)
{
var message = string.Format(
"Too much parameters in {0}.{1}(). Need {2}, got {3}",
type.Name,
method_name,
parameters_count,
provided_args_count
);
throw new ArgumentException(message);
}
var all_args = new object[parameters_count];
args.CopyTo(all_args, 0);
for (var i = provided_args_count; i < parameters_count; i++)
all_args = Missing.Value;
try
{
return method.Invoke(obj, all_args);
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
public static void Release(object com_object)
{
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(com_object) != 0) { }
}
}
{
var application = new Application();
try
{
var workbook = (Workbook)ComHelper.Invoke(application.Workbooks, "_Open", path);
try
{
var sheet = (Worksheet)workbook.Worksheets[1];
var range = sheet.UsedRange;
var result = new List<double>(range.Count);
foreach (Range value in range)
result.Add((double) value.Value2);
return result;
}
finally
{
ComHelper.Invoke(workbook, "Close");
}
}
finally
{
application.Quit();
ComHelper.Release(application);
}
}
static class ComHelper
{
public static object Invoke<T>(T obj, string method_name, params object[] args)
{
var type = typeof(T);
const BindingFlags binding_flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
var method = type.GetMethod(method_name, binding_flags);
if (method == null)
{
method = obj.GetType().GetMethod(method_name, binding_flags);
if (method == null)
throw new ArgumentException(string.Format("Method {0} not found in type {1}", method_name, type.Name));
}
var parameters_count = method.GetParameters().Length;
var provided_args_count = args.Length;
if (provided_args_count > parameters_count)
{
var message = string.Format(
"Too much parameters in {0}.{1}(). Need {2}, got {3}",
type.Name,
method_name,
parameters_count,
provided_args_count
);
throw new ArgumentException(message);
}
var all_args = new object[parameters_count];
args.CopyTo(all_args, 0);
for (var i = provided_args_count; i < parameters_count; i++)
all_args = Missing.Value;
try
{
return method.Invoke(obj, all_args);
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
public static void Release(object com_object)
{
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(com_object) != 0) { }
}
}
Время сократилось почти в три раза: 23 против 62 секунд, но это всё равно долго - уверен: можно быстрее. Замечательно здесь то, что само итерирование (foreach) - почти 40% этого времени. В общем, во вложении - лог (время в секундах) таймингов моего и вашего кода, посмотрите - мож чё и найдёте.
Можно ещё попробовать поставить ACE OLEDB 12 и выдернуть данные sql-запросом - сам не пробовал, но чем чёрт не шутит.
Комментарий к коду. Зачем использовать рефлекскию, когда в C# 4 есть dynamic и/или передача аргументов по имени?
Это C# 3 и давнишняя наработка :) Оно всё аинхрен к тому и сведётся, в конечном итоге.
http://www.codeproject.com/Questions/274445/Low-performance-when-reading-data-from-Excel-workb
Вот конечный вариант:
Код:
private void GetExcelData(string fullPath, List<double> arrForValues)
{
Excel.Application excelapp = new Excel.Application();
excelapp.Visible = false;
// to avoid appearing of Excel window on the screen
Excel.Workbook excelappworkbook = excelapp.Workbooks.Open(
fullPath,
Type.Missing, Type.Missing, true, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
Excel.Worksheet excelworksheet = (Excel.Worksheet)excelappworkbook.Worksheets.get_Item(1);
Excel.Range excelcells = excelworksheet.UsedRange;
object[,] worksheetValuesArray = excelcells.get_Value(Type.Missing);
for (int col = 1; col < (worksheetValuesArray.GetLength(1)+1); col++)
{
for (int row = 1; row < (worksheetValuesArray.GetLength(0)+1); row++)
{
arrForValues.Add((double) worksheetValuesArray[row, col]);
}
}
excelappworkbook.Close(false, Type.Missing, Type.Missing);
excelapp.Quit();
}
{
Excel.Application excelapp = new Excel.Application();
excelapp.Visible = false;
// to avoid appearing of Excel window on the screen
Excel.Workbook excelappworkbook = excelapp.Workbooks.Open(
fullPath,
Type.Missing, Type.Missing, true, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
Excel.Worksheet excelworksheet = (Excel.Worksheet)excelappworkbook.Worksheets.get_Item(1);
Excel.Range excelcells = excelworksheet.UsedRange;
object[,] worksheetValuesArray = excelcells.get_Value(Type.Missing);
for (int col = 1; col < (worksheetValuesArray.GetLength(1)+1); col++)
{
for (int row = 1; row < (worksheetValuesArray.GetLength(0)+1); row++)
{
arrForValues.Add((double) worksheetValuesArray[row, col]);
}
}
excelappworkbook.Close(false, Type.Missing, Type.Missing);
excelapp.Quit();
}
Позвольте я ещё дам совет: Never use 2 dots with COM objects!