我目前使用:
我正在使用以下代码将Excel工作表读入DataTable:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
string filename = "C:\\Users\\myusername\\Documents\\MyFile.xlsx";
DataTable dt = null;
try
{
string ExcelName = filename.Split(("\\").ToCharArray()[0])[filename.Split(("\\").ToCharArray()[0]).Length - 1].Split('.')[0];
string ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filename + ";Extended Properties=\"Excel 12.0;HDR=YES;IMEX=1;TypeGuessRows=0;ImportMixedTypes=Text\";";
string SheetName = CommonFunctions.GetExcelSheetNames(ConnectionString)[0];
using (OleDbConnection conn = new OleDbConnection(ConnectionString))
{
string query = string.Format("SELECT * FROM [" + SheetName + "]");
conn.Open();
using (OleDbCommand cmd = new OleDbCommand(query, conn))
{
using (OleDbDataReader rdr = cmd.ExecuteReader())
{
if (rdr.HasRows)
{
dt = new DataTable();
dt.TableName = ExcelName;
for (int i = 0; i < rdr.FieldCount; i++)
{
dt.Columns.Add(new DataColumn(rdr.GetName(i), typeof(string)));
}
while (rdr.Read())
{
DataRow dr = dt.NewRow();
for (int i = 0; i < rdr.FieldCount; i++)
{
dr[i] = rdr[i].ToString();
}
dt.Rows.Add(dr);
}
foreach (DataRow row in dt.Rows)
{
string s = row[0].ToString();
}
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
示例Excel工作表数据如下所示,以CSV格式提供,以便在Excel中轻松打开:
"store_number", "stock_code", "desired_quantity"
"64004", "BI_KRA_SEL_350065", "1"
"64004", "BI_KRA_SEL_500080", "1"
"86208", "BI_KRA_SEL_350065", "1"
"86208", "BI_KRA_SEL_500080", "1"
"64019", "BI_KRA_SEL_350065", "1"
"64019", "BI_KRA_SEL_500080", "1"
"85858", "BI_KRA_SEL_350065", "1"
"85858", "BI_KRA_SEL_500080", "1"
"72122", "BI_KRA_SEL_350065", "1"
"72122", "BI_KRA_SEL_500080", "1"
"68427", "BI_KRA_SEL_350065", "1"
"68427", "BI_KRA_SEL_500080", "1"
"79031", "BI_KRA_SEL_350065", "1"
"79031", "BI_KRA_SEL_500080", "1"
"67662", "BI_KRA_SEL_350065", "1"
"67662", "BI_KRA_SEL_500080", "1"
"92246", "BI_KRA_SEL_350065", "1"
"92246", "BI_KRA_SEL_500080", "1"
"85432", "BI_KRA_SEL_350065", "1"
"85432", "BI_KRA_SEL_500080", "1"
"87188", "BI_KRA_SEL_350065", "1"
"87188", "BI_KRA_SEL_500080", "1"
"91021", "BI_KRA_SEL_350065", "1"
"91021", "BI_KRA_SEL_500080", "1"
"79022", "BI_KRA_SEL_350065", "1"
"79022", "BI_KRA_SEL_500080", "1"
"86369", "BI_KRA_SEL_350065", "1"
"86369", "BI_KRA_SEL_500080", "1"
"67670", "BI_KRA_SEL_350065", "1"
"67670", "BI_KRA_SEL_500080", "1"
"92605", "BI_KRA_SEL_350065", "1"
"92605", "BI_KRA_SEL_500080", "1"
"92609", "BI_KRA_SEL_350065", "1"
"92609", "BI_KRA_SEL_500080", "1"
"92610", "BI_KRA_SEL_350065", "1"
"92610", "BI_KRA_SEL_500080", "1"
"92611", "BI_KRA_SEL_350065", "1"
"92611", "BI_KRA_SEL_500080", "1"
"92612", "BI_KRA_SEL_350065", "1"
"92612", "BI_KRA_SEL_500080", "1"
"92613", "BI_KRA_SEL_350065", "1"
"92613", "BI_KRA_SEL_500080", "1"
"92614", "BI_KRA_SEL_350065", "1"
"92614", "BI_KRA_SEL_500080", "1"
"92615", "BI_KRA_SEL_350065", "1"
"92615", "BI_KRA_SEL_500080", "1"
"92616", "BI_KRA_SEL_350065", "1"
"92616", "BI_KRA_SEL_500080", "1"
"w090", "BI_KRA_SEL_350065", "1"
"w090", "BI_KRA_SEL_500080", "1"
"C908", "BI_KRA_SEL_350065", "1"
"C908", "BI_KRA_SEL_500080", "1"
"w0901", "BI_KRA_SEL_350065", "1"
"w0901", "BI_KRA_SEL_500080", "1"
"G202", "BI_KRA_SEL_350065", "1"
"G202", "BI_KRA_SEL_500080", "1"
问题是第一列包含字母和/或空格。这些单元格在结果DataTable(dt)中显示为空白,即从“w090”到“G202”。
我发现当单元格格式为“常规”时会发生这种情况。但是,将这些单元格的格式更改为“文本”似乎可以解决问题。
我现在唯一的问题是我不能依赖我的客户提供将单元格设置为“文本”格式的文件。
是否有人知道这方面的修复方法,或者可能是使用“文字”格式克隆Excel文件的方法?
也许有人知道将Excel文件导入DataTables / DataSet的更智能方法。
非常感谢任何帮助。
答案 0 :(得分:2)
由于您使用Excel
作为数据库,因此每个字段(列)必须具有其确切的数据类型。 Excel
数据库驱动程序从第一个值猜测此类型。在您的情况下,第一列中的第一个值是数字。所以数据库在那里猜测数字数据类型。所以后来出现的字符串不适合那种类型。
数据库驱动程序有一个参数IMEX
,可以将所有数据视为文本。请参阅https://www.connectionstrings.com/ace-oledb-12-0/treating-data-as-text/。
所以试试
string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filename + ";Extended Properties=\"Excel 12.0;IMEX=1\";";
或
string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filename + ";Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1\";";
答案 1 :(得分:1)
这比使用adapter.Fill()
要多一点,但是如果你想对数据类型有更多的控制权,你可以预先声明它们,然后一次填充一行DataTable。因为它的Excel和Excel会很乐意混合和匹配列中的数据类型,所以我认为OleDb几乎不可能预先确定正确的数据类型。
以下是如何显式声明数据类型然后手动将其插入数据表的示例:
OleDbCommand cmd = new OleDbCommand(query, conn);
OleDbDataReader reader = cmd.ExecuteReader();
object[] fields = new object[reader.FieldCount];
for (int i = 0; i < fields.Length; i++)
dt.Columns.Add(new DataColumn(reader.GetName(i)));
dt.Columns[0].DataType = typeof(string);
dt.Columns[1].DataType = typeof(string);
dt.Columns[2].DataType = typeof(int);
while (reader.Read())
{
reader.GetValues(fields);
dt.Rows.Add(fields);
}
reader.Close();
- 编辑1/3/2017 -
以下是使用POCO的解决方案,我相信它会起作用:
如果您的POCO看起来像这样:
public class Stock
{
public string StoreNumber { get; set; }
public string StockCode { get; set; }
public double DesiredQuantity { get; set; }
}
此代码应从Excel读取数据并将其放入域对象列表中:
OleDbConnection conn = new OleDbConnection(ConnectionString);
conn.Open();
OleDbCommand cmd = new OleDbCommand(query, conn);
OleDbDataReader reader = cmd.ExecuteReader();
List<Stock> stockData = new List<Stock>();
while (reader.Read())
{
stockData.Add(new Stock()
{
StoreNumber = reader.GetValue(0).ToString(),
StockCode = reader.GetValue(1).ToString(),
DesiredQuantity = reader.GetDouble(2)
});
}
reader.Close();
我认为.GetString(x)
可能会因为您突出显示的问题而引发错误,但是通过使用.GetValue(x).ToString()
,您可以强制数据类型,知道它们都应该是字符串。
从这里开始,与数据表相比,我认为使用List<Stock>
将是一种乐趣。最好的部分是你对数据进行全面控制。
答案 2 :(得分:0)
使用 Interop 服务,这是一个很好的可靠解决方案。
添加引用: Microsoft.Office.Interop.Excel
版本12.0.0.0,可通过Nuget获取。
注意:请务必将完整路径作为文件名参数传递。
public static DataTable LoadExcelFile(string fileName, string worksheetName, int headerRowNumber, int firstDataRowNumber)
{
DataTable dt = new DataTable();
Microsoft.Office.Interop.Excel.Application ExcelApplication = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook ExcelWorkbook = ExcelApplication.Workbooks.Open(fileName, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Microsoft.Office.Interop.Excel.Worksheet ExcelWorksheet = null;
string WorksheetName = worksheetName;
if (string.IsNullOrWhiteSpace(worksheetName))
{
WorksheetName = ExcelWorkbook.ActiveSheet.Name;
}
ExcelWorksheet = (Microsoft.Office.Interop.Excel.Worksheet)ExcelWorkbook.Worksheets[WorksheetName];
dt.TableName = WorksheetName;
// Add the columns
Dictionary<string, int> Columns = new Dictionary<string, int>();
for (int i = 0; i < ExcelWorksheet.UsedRange.Columns.Count; i++)
{
string ColumnHeading = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)ExcelWorksheet.Cells[headerRowNumber, i + 1]).Value2);
if (!String.IsNullOrWhiteSpace(ColumnHeading) && !dt.Columns.Contains(ColumnHeading))
{
Columns.Add(ColumnHeading, i + 1);
dt.Columns.Add(ColumnHeading);
}
}
// Add the rows
for (int i = 0; i < ExcelWorksheet.UsedRange.Rows.Count - firstDataRowNumber + 1; i++)
{
try
{
int ColumnCount = 0;
DataRow Row = dt.NewRow();
bool RowHasContent = false;
foreach (KeyValuePair<string, int> kvp in Columns)
{
string CellContent = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)ExcelWorksheet.Cells[i + firstDataRowNumber, kvp.Value]).Value2);
Row[ColumnCount] = CellContent;
ColumnCount++;
if (!string.IsNullOrWhiteSpace(CellContent))
{
RowHasContent = true;
}
}
if (RowHasContent)
{
dt.Rows.Add(Row); ;
}
}
catch
{
}
}
// Clean up
try { ExcelWorksheet = null; } catch { }
try { ExcelWorkbook.Close(); } catch { }
try { ExcelWorkbook = null; } catch { }
try { ExcelApplication = null; } catch { }
return dt;
}
为什么要使用Interop服务?
使用基于Jet
或Ace
数据库引擎的解决方案时,Interop服务可以解决类型猜测和格式化错误。
错误解决方案的示例如下:
如果您目前使用上述解决方案之一,那么值得测试错误,请将成功的解决方案发布到此页面。
令人愉快的惊喜
使用Interop解决方案时,我预计在Excel中打开的文件与程序中打开的文件同名时会出现问题,但在测试期间我没有遇到过这样的问题。