使用Microsoft PersistentDictionary创建自定义结构体类型实例失败的问题咨询
我帮你梳理下问题的根源和解决办法:
首先解决编译错误:Serializable属性的误用
你提到添加[Serializable]时出现CS8124和IDE1007错误,这是因为你可能不小心写错了属性语法——正确的[Serializable]属性不需要额外的括号和内容(比如错误的[Serializable](Serializable)写法)。你的代码里已经引用了System命名空间(using System;),所以修正结构体定义即可:
[Serializable] public struct MyStruct { public bool ABoolean; public long ALong; }
这个语法错误修复后,编译问题就会消失,但还需要解决核心的运行时异常。
核心问题:自定义结构体不被Esent引擎支持
PersistentDictionary基于Esent数据库,它对值类型的支持有严格限制:
- 仅支持Esent原生兼容的值类型(比如
int、DateTime、bool、byte[]等) - 自定义类型要作为字典值,要么是标记了
[Serializable]的类(引用类型),要么是实现IEsentSerializable接口来自定义序列化逻辑
你的MyStruct是值类型(结构体),即便标记了[Serializable],Esent也无法将它映射到数据库列,因此抛出ArgumentOutOfRangeException。
可行的解决方案
方案1:把结构体改成类(最简单)
将MyStruct改为引用类型(类),保留[Serializable]属性,PersistentDictionary会自动处理序列化:
[Serializable] public class MyClass { public bool ABoolean; public long ALong; } // 实例化字典 PersistentDictionary<int, MyClass> pd2 = new PersistentDictionary<int, MyClass>("PersistentDict");
这样修改后,代码就能像DateTime版本一样正常运行。
方案2:手动序列化结构体为byte[]存储(保留结构体)
如果必须使用结构体,可以把它序列化为byte[],将字典类型定义为PersistentDictionary<int, byte[]>,读写时手动处理序列化:
// 序列化方法 private static byte[] SerializeMyStruct(MyStruct data) { using var ms = new MemoryStream(); var formatter = new BinaryFormatter(); formatter.Serialize(ms, data); return ms.ToArray(); } // 反序列化方法 private static MyStruct DeserializeMyStruct(byte[] data) { using var ms = new MemoryStream(data); var formatter = new BinaryFormatter(); return (MyStruct)formatter.Deserialize(ms); } // 使用示例 var pd2 = new PersistentDictionary<int, byte[]>("PersistentDict"); var myData = new MyStruct { ABoolean = true, ALong = 12345 }; pd2[1] = SerializeMyStruct(myData); // 读取数据 var storedData = DeserializeMyStruct(pd2[1]); pd2.Flush(); pd2.Dispose();
注意:在.NET Core/.NET 5+中使用BinaryFormatter需要安装System.Runtime.Serialization.Formatters.Binary NuGet包。
方案3:实现IEsentSerializable接口(高性能)
如果追求更好的性能,可以让结构体实现IEsentSerializable接口,自定义与Esent列的交互逻辑:
using Microsoft.Isam.Esent.Interop; public struct MyStruct : IEsentSerializable { public bool ABoolean; public long ALong; public void Serialize(JET_COLUMNID columnid, JET_SESID sesid, JET_TABLEID tableid) { // 将结构体字段序列化为字节流写入Esent列 using var ms = new MemoryStream(); using var writer = new BinaryWriter(ms); writer.Write(ABoolean); writer.Write(ALong); Api.SetColumn(sesid, tableid, columnid, ms.ToArray()); } public void Deserialize(JET_COLUMNID columnid, JET_SESID sesid, JET_TABLEID tableid) { // 从Esent列读取字节流并反序列化为结构体 var data = Api.RetrieveColumn(sesid, tableid, columnid); using var ms = new MemoryStream(data); using var reader = new BinaryReader(ms); ABoolean = reader.ReadBoolean(); ALong = reader.ReadInt64(); } }
这种方式需要熟悉Esent的API,但能避免通用序列化的开销,适合性能敏感的场景。
验证
选择任意一种方案修改后,你可以重复之前的测试流程,自定义类型的字典应该能正常创建、写入和释放资源了。
内容的提问来源于stack exchange,提问作者Rowan Smith

