Mở đầu

Bài viết này sẽ viết tổng thể một cách nhìn nhận việc đọc dữ liệu từ excel mà tác giả đã chính tay làm và kiểm nghiệm trong thời gian đi làm, cũng như nên dùng thư viện nào để đọc dữ liệu excel nhanh chóng nhât.Ở bài viết này mình sẽ đề cập đến hai bộ thư viện :

  1. Thư viện EPPlus mã nguồn mở.
  2. Thư viện Excel của Microsoft.

Excel Interop

 Image 6303d142 bss1c6 4c02 977c 56799557532e

Tất nhiên là một công cụ khá to lớn đã ra đời như một cụ tổ thì độ hoàn thiện các chức năng và các hàm không có gì để chê cả.Tuy nhiên việc đó đã khiến cho nó trở nên ì ạch hơn.Việc đọc tệp excel cũng phải mở tệp lên mới có thể thực hiện được việc đọc dữ liệu bên trong tệp.Điều này khiến cho việc đọc hàng loạt các tệp tốc độ tất nhiên sẽ khó mà qua mặt công cụ bên dưới được.

Thư viện này có đội ngũ bảo trì rất chuyên nghiệp.Với một cụ tổ to như vậy thì việc chuyên gia phân tích bảo trì sẽ ổn hơn rất nhiều.

Chính độ hoàn thiện của nó nên mình cũng chẳng phải lo đi nghiên cứu cách tạo ra mà nghiên cứu cách làm để phục vụ công việc của mình.

Mẫu ví dụ bên dưới đây mình sẽ đọc dữ liệu từ excel với hai kiểu đọc theo cột và theo dòng với hai tùy chọn.Danh sách trả về có thể là dánh sách list lồng list hoặc Datatable đều được.Tùy mỗi người mà việc tùy biến thư viện sẽ khác nhau.Và bạn sẽ thấy được mỗi lần đọc như vậy tệp excel sẽ được mở lên.

public static List<List<string>> ReadExcelByType(string filePath, string worksheetName, DataStorage type,
int Index = 1)
{
List<List<string>> data = new List<List<string>>();
// Open the Excel file and get the worksheet
Application excelApp = null;
bool alreadyOpen = false;
try
{
excelApp = (Application) Marshal.GetActiveObject("Excel.Application");
excelApp.ScreenUpdating = true;
excelApp.Visible = false;
alreadyOpen = true;
}
catch
{
}
if (excelApp == null)
{
excelApp = new Application();
excelApp.ScreenUpdating = true;
alreadyOpen = false;
}
Workbook workbook = null;
// See if the workbook is open already
foreach (Workbook wb in excelApp.Workbooks)
if (wb.FullName.ToLower() == filePath.ToLower())
{
workbook = wb;
break;
}
if (workbook == null)
workbook = excelApp.Workbooks.Open(filePath);
Worksheet worksheet = null;
foreach (Worksheet ws in workbook.Sheets)
if (ws.Name.ToLower() == worksheetName.ToLower())
{
worksheet = ws;
break;
}
// Read through the data in the excel file and add it to our data variable.
if (null != worksheet)
{
int rowCount = 0;
int colCount = 0;
// Get the total used range of the selected worksheet
Range usedRange = worksheet.UsedRange;
colCount = usedRange.Columns.Count;
rowCount = usedRange.Rows.Count;
for (int i = Index - 1; i <= rowCount; i++)
{
List<string> rowData = new List<string>();
Range cell = null;
switch (type)
{
case DataStorage.ByColumn:
for (int j = Index - 1; j <= colCount; j++)
{
cell = usedRange.Cells[i, j];
try
{
rowData.Add(cell.Text);
}
catch
{
rowData.Add(string.Empty);
}
}
break;
case DataStorage.ByRow:
for (int j = Index - 1; j <= rowCount; j++)
{
cell = usedRange.Cells[j, i];
try
{
rowData.Add(cell.Text);
}
catch
{
rowData.Add(string.Empty);
}
}
break;
}
data.Add(rowData);
}
}
// Close the workbook if it was opened via this command
if (alreadyOpen)
return data;
#region Close App
workbook.Close();
excelApp.Quit();
#endregion
// Send that sweet data home.
return data;
}

Lưu ý : Trong một vài trường hợp, khi mình sử dụng thư viện này mặc dù dữ liệu của mình không vượt ra ngoài giá trị của mảng nhưng vẫn bị báo lỗi.Các giả trị ở đây vẫn có giá trị xuất ra từ phần mềm thường được xem là nullable nhưng xử lí bị thiếu nên thành ra khi kéo vào mảng lại không nhận diện ra được.Cũng giống như việc một ô trống nhập giá trị Double(0) và một ô khởi tạo giá trị mói chưa có giá trị(Nullable) trong revit sau đó đưa về giá trị trống (Empty) vậy.Hôm nào mình có dịp sẽ giải thích rõ hơn về vấn đề này.

 Image 3391045a 6706 473d 8e09 d54e0930f25f

Nếu bạn có hứng thú với ông lớn này có thể thửthử đổi sang việc nghiên cứu đọc tệp với Open-XML-SDK sẽ phù hợp với dữ liệu lớn.

Console.WriteLine("start write excel :" + DateTime.Now.ToString("h:mm:ss tt") + "\n");
string fileName = @"D:\test.xlsx";
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
string text;
foreach (Row r in sheetData.Elements<Row>())
{
foreach (Cell c in r.Elements<Cell>())
{
text = c.CellValue.Text;
Console.Write(text + " ");
}
}
}
Console.WriteLine("end write excel :" + DateTime.Now.ToString("h:mm:ss tt") + "\n");
Console.ReadKey();
view raw ReadexcelOpenXml.cs hosted with ❤ by GitHub

EPPlus

50729751

Công cụ này phải nói có một điểm mạnh mình thích nhất là không phụ thuộc vào việc trên máy có đang cài đặt excel hay không.Ở một bài viết trước mình đã hướng dẫn đọc dữ liệu excel bằng Epplus trên Dynamo với Python, nếu bạn nào chưa xem có thể xem lại tại Đây.

Người dùng không cần cài bất cứ công cụ tiện ích nào để đọc được dữ liệu excel.Việc này khá tiện cho việc vừa làm việc với công cụ vừa sửa đổi dữ liệu excel nhanh chóng.Mình là một người thuần code chẳng hạn thì việc mở excel online không cài trên máy thì sao?

Tốc độ cũng vì đó mà tăng lên đáng kế.Phải nói là đọc nhanh hơn thư viện Excel Interop của Microsoft làm ra.

Tác giả có Open Source và có cả trả phí cho doanh nghiệp, điều này khiến cho việc quản lý dữ liệu và xem xét lỗi trở nên minh bạch và rõ ràng hơn.

Mẫu ví dụ bên dưới đây mình sẽ đọc dữ liệu từ excel với hai kiểu đọc theo cột và theo dòng với hai tùy chọn.Danh sách trả về có thể là dánh sách list lồng list hoặc datatable đều được.Tùy mỗi người mà việc tùy biến thư viện sẽ khác nhau.

public static List<List<string>> ReadExcelByType(string filePath, string worksheetName, DataStorage type,
int Index)
{
List<List<string>> data = new List<List<string>>();
ExcelPackage _excelPackage;
if (File.Exists(filePath))
{
FileInfo file = new FileInfo(filePath);
_excelPackage = new ExcelPackage(file);
}
else
{
return data;
}
if (_excelPackage == null)
return data;
if (!_excelPackage.Workbook.Worksheets.Select(x => x.Name).Contains(worksheetName))
return data;
ExcelWorksheet worksheet = _excelPackage.Workbook.Worksheets[worksheetName];
//check if the worksheet is completely empty
if (worksheet.Dimension == null)
return data;
if (type == DataStorage.ByColumn)
{
int rows = worksheet.Dimension.Rows;
int columns = worksheet.Dimension.Columns;
for (int i = Index; i <= rows; i++)
{
List<string> rowData = new List<string>();
for (int j = Index; j <= columns; j++)
rowData.Add(worksheet.Cells[i, j].Text);
data.Add(rowData);
}
}
else
{
//sheet range
int rows = worksheet.Dimension.Columns;
int columns = worksheet.Dimension.Rows;
for (int i = Index; i <= rows; i++)
{
List<string> rowData = new List<string>();
for (int j = Index; j <= columns; j++)
// MessageBox.Show(worksheet.Cells[i, j].Text);
rowData.Add(worksheet.Cells[j, i].Text);
data.Add(rowData);
}
}
return data;
}
}

Lưu ý : Đôi khi làm việc với thư viện này mình hay gặp một số lỗi khá nhức đầu ví dụ như lỗi Could not load file or assembly.... bên dưới đây không chỉ riêng gì thư viện này.Có rất nhiều nguyên nhân khiến cho việc tham chiếu không đọc được Assembly.

z2139799571013 83b243a6c21111866be4e4f28a6a3128

Mình cũng đã phải nghĩ nát cái đầu để khắc phục được lỗi khá khó chịu này.Việc này liên quan đến cả xác thực và phiên bản .NET nên mới gây ra lỗi này.Mình có thể sửa bằng cách.Giải pháp mình sửa được cho đến thời điểm này đó vẫn là cách chuyển đổi tham chiếu thư viện Reference sang PackageReference tại tệp .csproj ở mã nguồn.

Cũ :

<Reference Include="EPPlus, Version=5.4.0.0, Culture=neutral, PublicKeyToken=ea159fdaa78159a1, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\packages\EPPlus.5.4.0\lib\net45\EPPlus.dll</HintPath>
</Reference>

Sau khi thay đổi :

<PackageReference Include="EPPlus, Version=5.4.0.0, Culture=neutral, PublicKeyToken=ea159fdaa78159a1, processorArchitecture=MSIL" />

Ngoài ra có thể khắc phục với việc Load Assembly ngay trước khi mở Form.Tuy nhiên bạn không nên dùng cách load trực tiếp như này vì bạn còn phải bảo trì hệ thống của bạn, mỗi năm phần mềm sẽ cập nhật mới một phiên bản và con số sẽ thay đổi bất cứ lúc nào.Đây chỉ là cách sửa lỗi khi bạn đã gần rơi vào bế tắt.

void LoadAssambly()
{
try
{
Assembly SampleAssembly =
Assembly.Load("EPPlus, Version=5.4.0.0, Culture=neutral, PublicKeyToken=ea159fdaa78159a1");
}
catch (Exception e)
{
}
}
view raw LoadAssambly.cs hosted with ❤ by GitHub

Tổng kết

Nếu bạn là một người thích an toàn, sự ổn định và không quá chú trọng vào hiệu suất phần mềm thì nên dùng thư viện Excel Interop.Còn nếu bạn là người thích đam mê tốc độ và sự mới mẻ hướng mở thì hãy thử với EPPlus xem sao, biết đâu bạn sẽ thích nó. Ngoài ra còn rất nhiều thư viện ngoài kia mà mình không đề cập ở đây, bạn nghĩ sao về hai thư viện này, hãy bình luận bên dưới bài viết nhé!

Tham khảo

Medium/@RupaniChirag

EPPlus