0

问题

这部分是我成为自己最大的敌人。我有单元测试来验证我编写然后从 SQLite 数据库检索所有不同的基本数据类型的能力。在我的测试中,我验证了每种数据类型的几个不同值,包括(但不限于)<datatype>.MinValue<datatype>.MaxValue.

当我将 a 写入decimal.MaxValue数据库,然后尝试检索它时,我得到一个溢出异常(感谢数据库本身的舍入)。

注意我已经将我的实际类剥离到最简单的部分,并将它们放在一个测试方法中,这样我就可以更容易地展示所有内容。

private static SQLiteConnection connection;

[TestMethod()]
public void WriteDecimal()
{
  using (var cmd = new SQLiteCommand(connection))
  {
    cmd.CommandText = $"INSERT INTO foo(name, value) VALUES('bar', {decimal.MaxValue})";
    cmd.ExecuteNonQuery();

    cmd.CommandText = "SELECT * FROM foo;";

    using (SQLiteDataReader rdr = cmd.ExecuteReader())
    {
      while (rdr.Read())
      {
        Console.WriteLine($"{rdr.GetInt32(0)} {rdr.GetString(1)} {rdr.GetValue(2)}");
      }
    }
  }
}

#region Setup/Cleanup
[ClassInitialize()]
public static void Setup(TestContext context)
{
  FileInfo dbFile = new FileInfo(Path.Combine(Environment.GetEnvironmentVariable("temp"), @"\Sqlite\myDb.db"));
  dbFile.Directory.Create();
  dbFile.Delete();
  string connectionString = $"Data Source={dbFile?.FullName ?? ":memory:"}";
  connection = new SQLiteConnection(connectionString);
  connection.Open();

  using (var cmd = new SQLiteCommand(connection))
  {
    cmd.CommandText = @"CREATE TABLE foo(id INTEGER PRIMARY KEY, name TEXT, value Number)";
    cmd.ExecuteNonQuery();
  };
}

[ClassCleanup()]
public static void Cleanup()
{
  connection.Close();
}
#endregion

输出:

  Message: 
    Test method WriteDecimal threw exception:
    System.OverflowException: Value was either too large or too small for a Decimal.
  Stack Trace: 
    Number.ThrowOverflowException(TypeCode type)
    DecCalc.VarDecFromR8(Double input, DecCalc& result)
    IConvertible.ToDecimal(IFormatProvider provider)
    Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
    SQLite3.GetValue(SQLiteStatement stmt, SQLiteConnectionFlags flags, Int32 index, SQLiteType typ)
    SQLiteDataReader.GetValue(Int32 i)
    DatabaseDirect.WriteDecimal() line 54

解决方法

我找到了一种解决方法(我只是不喜欢它)。本质上,我让它失败了,然后返回并尝试将其作为 Double 抓取;然后将其转换为我需要的;因为它溢出我知道它必须是最大值或最小值:

using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
  while (rdr.Read())
  {
    decimal newVal;
    try
    {
      newVal = (decimal)rdr.GetValue(2);
    }
    catch (OverflowException)
    {
      double val = rdr.GetDouble(2);
      Type t = rdr.GetFieldType(2);
      newVal = val > 0 ? decimal.MaxValue : decimal.MinValue;
    }
    Console.WriteLine($"{rdr.GetInt32(0)} {rdr.GetString(1)} {newVal}");
  }
}

更大的问题(如我所见)

这不是我遇到这个问题的唯一地方。decimal.MinValue 和 ulong.MaxValue 也会发生这种情况。我并不完全喜欢我的解决方案,因为我只是假设如果有溢出我需要最大/最小值。我还想概括它,这样它就不会硬编码我可能需要的最小/最大值。再次,我找到了解决方案;但同样,它很丑(一个传递类型以将值转换为然后对其进行切换的函数......恶心)。

4

1 回答 1

0

您可能无法做到这一点,但是当我遇到小数溢出错误时,我尝试将我的 sqlite 列类型从小数更改为实数,从而消除了错误。

于 2020-12-08T14:12:47.407 回答