DbType.Time игнорируется для IDbDataParameter.DbType (SqlClient) - PullRequest
0 голосов
/ 10 июня 2019

У меня есть привычка всегда использовать общие абстракции в System.Data, такие как IDbCommand и IDbDataParameter, для доступа к данным вместо конкретных реализаций System.Data.SqlClient.SqlDbCommand и System.Data.SqlClient.SqlParameter, когда это возможно, даже если мое приложение предназначеноSQL Server.Я делаю это так, чтобы в будущем было проще портировать приложение на другую СУБД или, при необходимости, ввести уровни абстракции базы данных между моим приложением и SQL Server.Я успешно использую этот шаблон уже более десяти лет.

Сегодня, однако, я столкнулся с очень странным поведением, когда использование IDbDataParameter велось неожиданно.Используя код, подобный следующему:

System.Data.IDbCommand cmd;
TimeSpan someTimeSpanValue;
...
System.Data.IDbDataParameter dataParameter = cmd.CreateParameter();
dataParameter.DbType = DbType.Time;
dataParameter.Value = someTimeSpanValue;
cmd.Execute();

ADO.NET пожаловался, когда я выполнил свое заявление, что он не может преобразовать мой TimeSpan в DateTime.Базовый тип базы данных, который был установлен на основе этого параметра, имел тип TIME.Смущенный, я проследил код и, конечно же, когда я установил dataParameter.DbType = DbType.Time, а затем опросил dataParameter.DbType, он вернул DbType.DateTime вместо DbType.Time, как я его установил.

У меня есть обходной путь к этому коду, который выглядит следующим образом:

dataParameter.DbType = v.DbType; //v.DbType is the desire DbType for the command being prepared
if (v.DbType == DbType.Time && dataParameter.DbType != DbType.Time && dataParameter is System.Data.SqlClient.SqlParameter sqlParam)
    sqlParam.SqlDbType = SqlDbType.Time;

Кажется, когда я устанавливаю SqlDbType в SqlDbType.Time, а затем опрашиваю IDbDataParameter.DbType, он возвращает DbType.Time и все работает, хотя этот код теперь не так независим от базы данных, как хотелось бы.

Еще более странно, что если установить IDbDataParameter.DbType в DbType.Time (что является его текущим значением) после , установив SqlDbType в SqlDbType.Time, он все равно изменится на DbType.DateTime.

Мой вопрос, после всего этого, заключается в том, является ли это дефектом в реализации System.Data.SqlClient IDbDataParameter или есть какая-то настройка, о которой я не знаю, которая вызывает такое поведение (возможно, по умолчанию предварительноСовместимость с -SQL 2008), которую можно настроить, чтобы избежать этого хакерского обходного пути?

1 Ответ

1 голос
/ 10 июня 2019

Спасибо @DavidG, который разместил комментарий, ссылающийся на исходный код .NET. На основании ссылочного кода:

    override public DbType DbType {
        get {
            return GetMetaTypeOnly().DbType;
        }
        set {
            MetaType metatype = _metaType;
            if ((null == metatype) || (metatype.DbType != value) ||
                    // SQLBU 504029: Two special datetime cases for backward compat
                    //  DbType.Date and DbType.Time should always be treated as setting DbType.DateTime instead
                    value == DbType.Date ||
                    value == DbType.Time) {
                PropertyTypeChanging();
                _metaType = MetaType.GetMetaTypeFromDbType(value);
            }
        }
    }

Я могу сделать вывод, что это поведение разработано на основе комментариев в ссылочном коде.

Вслед за GetMetaTypeFromDbType(value) опорным источником может показаться, что это согласовано:

  internal static MetaType GetMetaTypeFromDbType(DbType target) {
        // if we can't map it, we need to throw
        switch (target) {
        case DbType.AnsiString:             return MetaVarChar;
        case DbType.AnsiStringFixedLength:  return MetaChar;
        case DbType.Binary:                 return MetaVarBinary;
        case DbType.Byte:                   return MetaTinyInt;
        case DbType.Boolean:                return MetaBit;
        case DbType.Currency:               return MetaMoney;
        case DbType.Date:
        case DbType.DateTime:               return MetaDateTime;
        case DbType.Decimal:                return MetaDecimal;
        case DbType.Double:                 return MetaFloat;
        case DbType.Guid:                   return MetaUniqueId;
        case DbType.Int16:                  return MetaSmallInt;
        case DbType.Int32:                  return MetaInt;
        case DbType.Int64:                  return MetaBigInt;
        case DbType.Object:                 return MetaVariant;
        case DbType.Single:                 return MetaReal;
        case DbType.String:                 return MetaNVarChar;
        case DbType.StringFixedLength:      return MetaNChar;
        case DbType.Time:                   return MetaDateTime;
        case DbType.Xml:                    return MetaXml;
        case DbType.DateTime2:              return MetaDateTime2;
        case DbType.DateTimeOffset:         return MetaDateTimeOffset;
        case DbType.SByte:                  // unsupported
        case DbType.UInt16:
        case DbType.UInt32:
        case DbType.UInt64:
        case DbType.VarNumeric:
        default:                            throw ADP.DbTypeNotSupported(target, typeof(SqlDbType)); // no direct mapping, error out
        }
    }

Исходя из этого, может показаться, что реализация IDbDataParameter.DbType в SqlParameter просто не поддерживает DbType.Time или какой-либо способ создания параметра типа TIME с использованием интерфейса IDbDataParameter, поскольку ни один из них дела возвращаются MetaTime. Я делаю вывод, что единственный способ получить параметр для ссылки на тип TIME - использовать вместо него SqlParameter.SqlDbType.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...