我正在尝试序列化内存中的 Sqlite 数据库,以便稍后可以将其转换回文件。我了解如何在 node.js 中序列化数据库“db.serialize”如何在“node-sqlite3”和 C 中工作在 C 中序列化数据库。我只是无法找到一种方法来在 C# 中完成此任务。如果有人有任何见解,将不胜感激!
到目前为止我所拥有的
using System.Data.SQLite;
using Newtonsoft.Json;
using System.Text;
public byte[] SerializeDB(string sql)
{
using(var con = new SQLiteConnection($"Data Source=:memory:;Version=3;New=True;")) {
con.Open();
using(SQLiteCommand cmd = new SQLiteCommand(sql, con)) {
cmd.ExecuteNonQuery();
return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(**WHAT SHOULD GO HERE**));
}
}
}
SQLite 定义了一个方法作为其 C 接口的一部分来执行此操作:
https://www.sqlite.org/c3ref/serialize.html
unsigned char *sqlite3_serialize(
sqlite3 *db, /* The database connection */
const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */
sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */
unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */
);
sqlite3_serialize(D,S,P,F) 接口返回一个指向内存的指针,该指针是数据库连接 D 上的 S 数据库的序列化但是,我正在使用的 C# 库(sqlite-net-base,以 SQLitePCLRaw.provider.winsqlite3 作为提供程序)不会 DllImport 将此方法作为其提供程序实现的一部分。尽管 SQLite 规范包含该方法,但该方法仍然可用。对于普通的磁盘数据库文件来说,序列化只是磁盘文件的副本。对于内存数据库或“TEMP”数据库,如果该数据库备份到磁盘,则序列化与写入磁盘的字节序列相同。
因此,我所做的就是自己将方法与库一起导入。这在我的例子中有效(使用 winsqlite3)。您应该能够通过将 DLL 名称更改为您正在使用的名称来执行相同的操作。
[DllImport("winsqlite3", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
public static extern IntPtr sqlite3_serialize(sqlite3 db, [MarshalAs(UnmanagedType.LPStr)] string a, IntPtr piSize, uint mFlags);
在文档中有一个重要的注释:
调用者负责释放返回值以避免内存泄漏由于这是调用 C 代码,因此请务必记住,C# 不会为我们进行垃圾收集,直到我们获得它返回给 C# 托管内存的任何内存为止。
因此,可以使用一种方法来包装此调用并使其对 C# 更加友好:
private byte[] SeralizeDb()
{
var lengthInput = Marshal.AllocHGlobal(sizeof(long));
var unmanagedResult = sqlite3_serialize(this.scannerSqliteDb.Handle, "main", lengthInput, 0); // https://www.sqlite.org/c3ref/serialize.html main is mentioned as an example and is what works
// Handle null result or a bad length.
var lengthResult = (int)Marshal.ReadInt64(lengthInput); // Cast as int for the Marshel.Copy. Db is only a cache of a few Mb ever in our case
var managedResult = new byte[lengthResult];
Marshal.Copy(unmanagedResult, managedResult, 0, lengthResult);
// Free unmanaged memory allocations
Marshal.FreeHGlobal(lengthInput);
Marshal.FreeHGlobal(unmanagedResult);
return managedResult;
}