问题
这部分是我成为自己最大的敌人。我有单元测试来验证我编写然后从 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 也会发生这种情况。我并不完全喜欢我的解决方案,因为我只是假设如果有溢出我需要最大/最小值。我还想概括它,这样它就不会硬编码我可能需要的最小/最大值。再次,我找到了解决方案;但同样,它很丑(一个传递类型以将值转换为然后对其进行切换的函数......恶心)。