Odbc使用internalGetDate将String解析为Date?

时间:2015-12-02 17:50:11

标签: c# .net-4.0 odbc

我试图将我为SQL Server连接编写的一些代码转换为使用Odbc数据源。我在OdbcDataReader.GetValue(int)尝试为字符串字段调用OdbcDataReader.internalGetDate时遇到了一些问题。

以下是我目前用于获取值的代码:

private static void ReadRecord<T>(IDataRecord record, T myClass)
{
    .... inside loop of datarecord fields ....
        var value = record.GetValue(i);
        pi.SetValue(myClass,
            value == DBNull.Value
                ? null
                : Convert.ChangeType(value, record.GetFieldType(i)), null);

当我使用Odbc对特定的4D数据表执行此操作时,我得到一个没有附加Message的OdbcException。异常堆栈跟踪显示使用了internalGetDate。

at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode)
at System.Data.Odbc.OdbcDataReader.GetData(Int32 i, SQL_C sqlctype, Int32 cb, Int32& cbLengthOrIndicator)
at System.Data.Odbc.OdbcDataReader.GetData(Int32 i, SQL_C sqlctype)
at System.Data.Odbc.OdbcDataReader.internalGetDate(Int32 i)
at System.Data.Odbc.OdbcDataReader.GetValue(Int32 i, TypeMap typemap)
at System.Data.Odbc.OdbcDataReader.GetValue(Int32 i)
at System.Data.Odbc.DbCache.AccessIndex(Int32 i)
at System.Data.Odbc.OdbcDataReader.GetValue(Int32 i)

我查看了引用源here,它显示执行GetSqlType以确定在稍后的GetValue()调用中调用的函数。我写了这段代码来检查有问题的索引的SqlType

var odbcdatareader = typeof(OdbcDataReader);
var method = odbcdatareader.GetMethod("GetSqlType", BindingFlags.Instance | BindingFlags.NonPublic);
var type = method.Invoke(record, new object[] { i });
var typemap = odbcdatareader.Assembly.GetType("System.Data.Odbc.TypeMap");
var typemapodbctype = typemap.GetField("_odbcType", BindingFlags.Instance | BindingFlags.NonPublic);
var typemapdbtype = typemap.GetField("_dbType", BindingFlags.Instance | BindingFlags.NonPublic);
var typemaptype = typemap.GetField("_type", BindingFlags.Instance | BindingFlags.NonPublic);
var typemapsqltype = typemap.GetField("_sql_type", BindingFlags.Instance | BindingFlags.NonPublic);

Console.WriteLine("{0}, {1}, {2}, {3}", typemapodbctype.GetValue(type),
    typemapdbtype.GetValue(type), typemaptype.GetValue(type),
    typemapsqltype.GetValue(type));

此检查的结果是:

Char, AnsiStringFixedLength, System.String, CHAR

为什么Odbc的GetSqlType会将此报告为CHAR,然后使用internalGetDate尝试解析数据?我错过了一些明显的东西吗?

即使是weirder,当我在该索引上调用GetString()时,我也会在internalGetDate()时出错。

at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode)
at System.Data.Odbc.OdbcDataReader.GetData(Int32 i, SQL_C sqlctype, Int32 cb, Int32& cbLengthOrIndicator)
at System.Data.Odbc.OdbcDataReader.GetData(Int32 i, SQL_C sqlctype)
at System.Data.Odbc.OdbcDataReader.internalGetDate(Int32 i)
at System.Data.Odbc.OdbcDataReader.GetValue(Int32 i, TypeMap typemap)
at System.Data.Odbc.OdbcDataReader.GetValue(Int32 i)
at System.Data.Odbc.DbCache.AccessIndex(Int32 i)
at System.Data.Odbc.OdbcDataReader.internalGetString(Int32 i)
at System.Data.Odbc.OdbcDataReader.GetString(Int32 i)

1 个答案:

答案 0 :(得分:0)

此问题实际上与另一个问题有关,其中数据库服务器(在本例中为4D)将空日期与00/00/0000 12:00:00 AM相关联。 DateTime无法解析此日期,因此OdbcDataReader崩溃。我通过捕获异常并将我的类的属性设置为null来解决第一个问题,但这并没有解决根本问题。由于异常,DbCache没有缓存该值,并且由于OdbcDataReader and DbCache works的原因,系统必须遍历每个字段并获取它的数据。这是潜在的错误。 GetString循环遍历0..i并在internalGetDate上为i-1字段崩溃。

在我看来,决议是......非常难看,但却符合我的目的。

catch (ArgumentOutOfRangeException ex)
{
    if (ex.Message == "Year, Month, and Day parameters describe an un-representable DateTime.")
    {
        pi.SetValue(myClass, null, null);
        var odbcdatareader = typeof(OdbcDataReader);
        var dataCache = odbcdatareader.GetField("dataCache",
            BindingFlags.Instance | BindingFlags.NonPublic);
        var dbcache = dataCache.GetValue(record);
        var valuesfield = dbcache.GetType()
            .GetField("_values", BindingFlags.Instance | BindingFlags.NonPublic);
        var values = (object[]) valuesfield.GetValue(dbcache);
        values[i] = new DateTime(1, 1, 1);
        valuesfield.SetValue(dbcache, values);
    }
    else
        throw;
}