如何强制ADO.Net在读取器TableSchema中仅使用System.String DataType

我正在使用OleDbConnection查询Excel 2007电子表格。 我想迫使OleDbDataReader只使用string作为列的数据types。

系统正在查看前8行数据并推断数据types为Double。 问题是,在第9行我有一个string在该列和OleDbDataReader返回一个空值,因为它不能转换为双。

我已经使用这些连接string:

Provider = Microsoft.ACE.OLEDB.12.0; Data Source =“ExcelFile.xlsx”; Persist Security Info = False; Extended Properties =“Excel 12.0; IMEX = 1; HDR = No”

Provider = Microsoft.Jet.OLEDB.4.0; Data Source =“ExcelFile.xlsx”; Persist Security Info = False; Extended Properties =“Excel 8.0; HDR = No; IMEX = 1”

看着reader.GetSchemaTable()。行[7] .ItemArray [5],它的dataType是Double。

此架构中的第7行与Excel中的特定列相关,我遇到了问题。 ItemArray [5]是它的DataType列

是否有可能为读者创build一个自定义的TableSchema,所以当访问ExcelFiles时,我可以将所有单元格视为文本,而不是让系统尝试推断数据types?


我在这个页面find了一些很好的信息: 使用ADO.NET阅读Excel电子表格的技巧

关于ADO.NET接口的主要怪癖是如何处理数据types。 (你会注意到我一直在仔细地避免在阅读电子表格时返回哪些数据types的问题。)你准备好了吗? ADO.NET扫描前8行数据,并基于这个猜测每列的数据types。 然后它试图强制从该列到该数据types的所有数据,每当强制失败时返回NULL!

谢谢,
基思


这里是我的代码的简化版本:

using (OleDbConnection connection = new OleDbConnection(BuildConnectionString(dataMapper).ToString())) { connection.Open(); using (OleDbCommand cmd = new OleDbCommand()) { cmd.Connection = connection; cmd.CommandText = SELECT * from [Sheet1$]; using (OleDbDataReader reader = cmd.ExecuteReader()) { using (DataTable dataTable = new DataTable("TestTable")) { dataTable.Load(reader); base.SourceDataSet.Tables.Add(dataTable); } } } } 

正如你所发现的那样,OLEDB使用的Jet的方式是可以调整的。 如果设置为使用OleDbConnection从Excel文件读取,则需要将HKLM\...\Microsoft\Jet\4.0\Engines\Excel\TypeGuessRows值设置为零,以便系统将扫描整个结果集。

也就是说,如果您打算使用替代引擎从Excel文件读取数据,则可以考虑使用ExcelDataReader 。 它将所有列作为string读取,但可让您使用dataReader.Getxxx方法获取types化值。 这是一个填充DataSet的示例:

 DataSet result; const string path = @"....\Test.xlsx"; using ( var fileStream = new FileStream( path, FileMode.Open, FileAccess.Read ) ) { using ( var excelReader = ExcelReaderFactory.CreateOpenXmlReader( fileStream ) ) { excelReader.IsFirstRowAsColumnNames = true; result = excelReader.AsDataSet(); } } 

看看这个页面上的最终答案。


刚注意到你所指的页面也是这样说的


更新

问题似乎是与JET引擎本身,而不是ADO。 一旦JET决定这种types,就坚持下去。 之后做的任何事情都没有影响; 比如将值转换为SQL中的string(例如Cstr([Column]))只会导致返回空string。

在这一点上(如果没有其他答案),我会select其他方法:修改电子表格; 修改registry(不理想,因为你会搞乱设置为每个其他应用程序使用JET); Excel自动化或不使用JET的第三方组件。

如果自动化选项要慢,那么也许只是使用它来保存电子表格,这是更容易处理的不同格式。

注意64位操作系统它在这里:

 My Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Jet\4.0\Engines\Excel 

我面临同样的问题,并认定这是许多人经常遇到的事情。 以下是一些已经提出的解决scheme,其中许多我已经尝试实施:


  1. 将以下内容添加到连接string( Source )中:

TypeGuessRows = 0; ImportMixedTypes =文本

  1. 将以下内容添加到连接string( Source , More Discussion , 甚至更多 ):

IMEX = 1; HDR = NO;

  1. 编辑以下registry设置,禁用“TypeGuessRows”和“ImportMixedTypes”设置为“文本”( 来源 , 不推荐 , 更多文档 ):

Hkey_Local_Machine / Software / Microsoft / Jet / 4.0 / Engines / Excel / TypeGuessRows Hkey_Local_Machine / Software / Microsoft / Jet / 4.0 / Engines / Excel / ImportMixedTypes

  1. 考虑使用另一个库来读取excel文件:

    • EPPlus
    • ExcelDataReader (也build议是@Thomas)
    • OPENXML
  2. 将源文件中的所有数据格式化为文本(至less前8行),尽pipe我明白这通常是不切实际的( Source ,尽pipe这与SSIS有关系,但它们是相同的概念)

  3. 在导入文件之前使用Schema.ini文件来定义数据types,我发现这与直接使用“Jet.OleDb”相关,可能需要修改连接string。 这可能只适用于CSV的我还没有尝试过这种方法。( 来源 , 相关文章 )


这些都没有为我工作(虽然我相信他们已经为别人工作)。 我认为@Asher表示,这个问题确实没有很好的解决scheme。 在我的软件中,我只是向用户显示一条错误消息(如果任何需要的列包含空值),指示他们将所有列格式化为“文本” 。

老实说,我认为这本书更适合于情况。 这个问题已经多次提出:

  • “目的地的数据types是varchar,但是假设的数据types是”double“,将使任何不适合的数据无效。”( Source )

  • “但问题实际上是在OLEDBDataReader中,问题是如果它看起来大部分是列中的数字,那么它就假定一切都是一个数字 – 如果一个正在读取的行项不是一个数字,它就会将它设置为null! “( 来源 )

  • “问题似乎与JET引擎本身而不是ADO有关,一旦JET决定这种types,它就会坚持下去。”(@ Asher)

虽然我还没有发现任何以官方身份logging的这些信息,但我认为这是一个有意的devise决定,也是Jet数据库库的工作原理。 我毫不犹豫地称这个库完全无用,因为我认为对于很多人来说,其中一些解决scheme确实有效,但到目前为止,我的结论是,这个库不能读取单个列中的多种数据types,并且不适合用于一般数据检索。