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

Posted

技术标签:

【中文标题】如何强制 ADO.Net 在阅读器 TableSchema 中仅使用 System.String DataType【英文标题】:How to force ADO.Net to use only the System.String DataType in the readers TableSchema 【发布时间】:2011-02-03 18:59:00 【问题描述】:

我正在使用 OleDbConnection 来查询 Excel 2007 电子表格。我想强制 OleDbDataReader 仅使用字符串作为列数据类型。

系统正在查看前 8 行数据并推断数据类型为 Double。问题是,在第 9 行,我在该列中有一个字符串,并且 OleDbDataReader 返回 Null 值,因为它无法转换为 Double。

我使用过这些连接字符串:

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().Rows[7].ItemArray[5],它的dataType是Double。

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

是否可以为阅读器创建自定义 TableSchema,以便在访问 ExcelFiles 时,我可以将所有单元格视为文本,而不是让系统尝试推断数据类型?


我在这个页面找到了一些很好的信息:Tips for reading Excel spreadsheets using ADO.NET

关于 ADO.NET 接口的主要怪癖是如何处理数据类型。 (您会注意到,在阅读电子表格时,我一直在小心避免返回哪些数据类型的问题。)您准备好了吗? ADO.NET 扫描前 8 行数据,并据此猜测每列的数据类型。然后它会尝试将该列中的所有数据强制转换为该数据类型,只要强制失败就返回 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);
            
        
    

【问题讨论】:

你能贴出你从电子表格中读取数据的代码吗? 感谢您提供的代码。在我看来没问题。我自己完成了这个 ADO excel 阅读器,没有任何问题。当你明确告诉它使用数据值作为字符串时,它会给你任何错误吗? celldata = reader[i][i].ToString() 或者,如果您使用的是 gridview 或中继器(我怀疑),是否将您的数据显式转换为字符串? 数据读取器本身将有问题的单元格作为 dbNull 返回。查看问题中关于 1/2 way down 的链接,它很好地解释了问题。 【参考方案1】:

正如您所发现的,OLEDB 使用 Jet,它的调整方式受到限制。如果您设置为使用 OleDbConnection 从 Excel 文件中读取数据,则需要将 HKLM\...\Microsoft\Jet\4.0\Engines\Excel\TypeGuessRows 值设置为零,以便系统扫描整个结果集。

也就是说,如果您愿意使用替代引擎来读取 Excel 文件,则可以考虑尝试ExcelDataReader。它将所有列作为字符串读取,但允许您使用 dataReader.Getxxx 方法来获取类型值。这是一个填写 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();
    

【讨论】:

谢谢,我现在正在测试。它确实存在一些问题,即多个具有文本的单元格被视为空值。 我正在使用 ExcelDataReader v.2.0.1.0 但与***.com/questions/2249023 有相同的问题。我从exceldatareader.codeplex.com/Project/Download/… 应用了待处理的补丁,这似乎已经修复了错误。 ExcelDataReader 是我原来问题的有效解决方案。谢谢 发现 ExcelDataReader 的更多问题。详细信息并在此处修复:exceldatareader.codeplex.com/WorkItem/View.aspx?WorkItemId=5910 @Keith Sirmons - 对了。谢谢! 如何使用 ExcelDataReader 指定特定的列类型?我有 Excel 表,其中一列包含日期值,但它的类型是字符串。生成数据集时,我需要将该字符串转换为日期...【参考方案2】:

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

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

【讨论】:

然后将TypeGuessRows设置为0【参考方案3】:

在this page 上查看最终答案。


刚刚注意到您引用的页面说了同样的话......


更新

问题似乎出在 JET 引擎本身而不是 ADO。一旦 JET 决定了类型,它就会坚持下去。之后所做的任何事情都没有效果;就像在 SQL 中将值转换为字符串(例如 Cstr([Column]))只会导致返回一个空字符串。

此时(如果没有其他答案)我会选择其他方法:修改电子表格;修改注册表(不理想,因为您将弄乱使用 JET 的所有其他应用程序的设置); Excel 自动化或不使用 JET 的第三方组件。

如果自动化选项变慢,那么也许只需使用它以更易于处理的不同格式保存电子表格。

【讨论】:

是的。这与我提供的链接信息相同,但不适用于我的情况。谢谢你,基思【参考方案4】:

我遇到了同样的问题,并确定这是许多人普遍遇到的问题。以下是一些建议的解决方案,其中许多我都尝试过实施:


    将以下内容添加到您的连接字符串(Source):

TypeGuessRows=0;ImportMixedTypes=Text

    将以下内容添加到您的连接字符串中(Source、More Discussion、Even More):

IMEX=1;HDR=否;

    编辑以下注册表设置,禁用“TypeGuessRows”,并将“ImportMixedTypes”设置为“Text”(Source、Not Recommended、More Documentation):

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

    考虑使用替代库来读取 excel 文件:

    EPPlus ExcelDataReader(也建议使用@Thomas) OpenXml

    将源文件中的所有数据格式化为文本(至少前 8 行),虽然我知道这通常是不切实际的(Source,虽然这与 SSIS 有关,但它是相同的概念)

    在导入文件之前使用 Schema.ini 文件定义数据类型,我发现这与直接使用“Jet.OleDb”有关,可能需要您修改连接字符串。这可能只适用于 CSV 我没有尝试过这种方法。(Source, Related Post)


这些都不适合我(尽管我相信它们也适合其他人)。我认为@Asher 表达的观点是,这个问题确实没有好的解决方案。在我的软件中,我只是向用户显示一条错误消息(如果任何必需的列包含空值),指示他们使用format all columns as "Text"。

老实说,我认为this book 更适用于情况。已经多次说明的问题是:

"目的地的数据类型是 varchar 但假设的数据 “double”类型会使任何不适合的数据无效。”(Source)

"但问题实际上出在 OLEDBDataReader 上。问题 是如果它在列中看到的主要是数字,它会假设一切 是一个数字 - 如果正在读取的行项目不是一个数字,它只是 将其设置为空!哎哟!”(Source)

“问题似乎出在 JET 引擎本身而不是 ADO。曾经 JET 决定类型,并坚持下去。”(@Asher)

虽然我没有发现任何以官方身份记录的内容,但我认为很明显这是一个有意的设计决定,并且只是 Jet Database Library 的工作原理。我毫不犹豫地称这个库完全没用,因为我认为对于很多人来说,其中一些解决方案确实有效,但到目前为止,对于我的项目,我得出的结论是,这个库不能在单个列中读取多种数据类型并且不适合用于一般数据检索。

【讨论】:

以上是关于如何强制 ADO.Net 在阅读器 TableSchema 中仅使用 System.String DataType的主要内容,如果未能解决你的问题,请参考以下文章

CQRS读端ORM还是纯ADO.NET?

ADO.NET Entity Framework 的实际好处是啥?

如何使用 ADO.net 在视图中包含相关对象

ADO.NET Entity Framework 如何查看T

如何使用 MS Access 作为 ADO.NET 实体框架的提供者?

在.NET Framework 应用程序中使用ADO.NET访问数据库