我对.Net中的Sqlite没有太多经验,但我看到的行为相当奇怪。假设我们有一个带有以下project.json
的.Net核心应用程序:
{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.Data.Sqlite": "1.0.0",
"Dapper": "1.50.2"
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},
"imports": "dnxcore50"
}
}
}
我们还有一个简单的类Item
:
public class Item
{
public Item() { }
public Item(int id, string name, decimal price)
{
this.Id = id;
this.Name = name;
this.Price = price;
}
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
然后我创建一个内存数据库并用数据填充它(使用Dapper):
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
connection.Execute("CREATE TABLE IF NOT EXISTS Items(Id INT, Name NVARCHAR(50), Price DECIMAL)");
var items = new List<Item>
{
new Item(1, "Apple", 3m),
new Item(2, "Banana", 1.4m)
};
connection.Execute("INSERT INTO Items(Id, Name, Price) VALUES (@Id, @Name, @Price)", items);
然后我尝试从Items
表中读取:
var dbItems = connection.Query<Item>("SELECT Id, Name, Price FROM Items").ToList();
当我运行解决方案时,我得到以下异常:
未处理的异常:System.InvalidOperationException:解析第2列时出错(Price = 1.4 - Double)---> System.Invali dCastException:无法将类型为'System.Double'的对象强制转换为'System.Int64'。
好的,然后我尝试使用Microsoft.Data.Sqlite
来获取数据:
var command = connection.CreateCommand();
command.CommandText = "SELECT Price FROM Items";
var reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader[0].GetType());
}
结果我得到:
System.Int64 // Price = 3
System.Double // Price = 1.4
我尝试使用十进制价格在真实数据库上运行查询,返回的数据类型是正确的,并且始终是十进制的(如预期的那样)。
我应该进一步挖掘什么方向?我的内存数据库有问题吗?如何使其与小数一致?
在SQLite中,如果创建具有数字/十进制列数据类型的表,则它与数值类型相关性关联 ,其默认行为是在值没有小数时存储整数,或者如果值包含小数则存储实数。这有助于节省字节存储,因为最常用的整数值(小到中等应用程序的值不到一百万)需要的字节数比实际数据类型少:
整数(约):
反对8字节真实IEEE数据类型
如果你需要总是存储一个实数,就必须声明一个float / real数据类型,以便被认为是真正的类型亲和性,因此即使你向SQLite发送一个整数,它也会被存储为真实的
资料来源: https : //sqlite.org/datatype3.html
我知道真实可能是不精确的,但我在wal模式下使用C#Decimal数据类型和SQLite与磁盘中的数据库进行了一些测试,如果所有操作都是在C#中进行的,我从未遇到过舍入错误。但是当我直接在SQLite中进行一些计算时,由于2个整数除法,我得到了一些舍入误差和一些不好的计算
我使用EF6和Dapper读取/写入数值(来自SQLite的整数/实数)并且从未出现错误。
现在,我打算利用SQLite数字行为来使用dapper TypeHandler来节省磁盘空间,因此我可以复制SQL Server Money
实现(在内部,SQL Server保存一个8字节的整数,当它加载到内存时它除以10000) :
public class NumericTypeHandler : SqlMapper.TypeHandler<decimal>
{
public override void SetValue(IDbDataParameter parameter, decimal value)
{
parameter.Value = (long)(value / 10000);
}
public override decimal Parse(object value)
{
return ((decimal)value)/10000M;
}
}
另外,我设置了datetime UnixEpoch实现来节省磁盘空间并提高性能
注意:
SQLite可以在低需求的asp.net应用程序中处理数十个用户,诀窍是使用pragma指令调整SQLite和其他东西:
我还在C#方面做了一些改进:
避免保存不必要的数据(错误日志,电子邮件html文本)
我认为如果你计划使用SQLite作为后端,你应该使用像EFx或Dapper这样的ORM。使用精巧的工具,您需要创建自己的微框架以节省开发时间(基本的CRUD功能和Linq查询转换)。
我发现的最好的工具是https://github.com/linq2db/linq2db ,你使用linq访问数据库具有非常好的速度和linq的容易性。并且可以实现CRUD功能。
如果这个答案有帮助,我会很高兴的
问候