尝试使用 protobuf-net 序列化 System.Numerics.Quaternion
Trying to serialize System.Numerics.Quaternion using protobuf-net
背景信息
我有一个 .Net Standard 2.0 class 库,它使用 Protobuf-net.grpc 的代码优先方法来定义 gRPC 服务。在这个定义中,我有 classes 定义了我们用来记录传感器数据并使用 protobuf-net 将其序列化的不同数据结构。我的程序正在摄取数十万个大型对象/秒(很快就会扩展到数百万个)并且旨在用于嵌入式环境。
问题
在我下面的 class 中,我想加入一个 System.Numerics.Quaterion 作为成员。我似乎无法将其序列化。使用静态构造函数,RuntimeTypeModel 会抛出异常,因为在执行静态构造函数时已经以某种方式创建了四元数模型。因为这是一个 class 库,我非常想避免在使用 gRPC 服务的每个不同程序中调用 RuntimeTypeModel。我希望找到一种方法来序列化 System.Numerics.Quaternion.
我曾尝试将此静态构造函数运行时定义置于 class 层次结构的最高级别,但无济于事。仍然抛出异常。
[ProtoContract]
public class IMUData : SensorData, ISensorData
{
static IMUData()
{
RuntimeTypeModel.Default.Add(typeof(Quaternion), false)
.Add("W")
.Add("X")
.Add("Y")
.Add("Z");
}
... //Other members
[ProtoMember(8)]
public Quaternion Orientation
{
get; set;
}
... //Other methods and members
}
问题
我想做的事情是否可行,或者我应该简单地创建自己的四元数 class 并定义隐式运算符? (我宁愿避免这种情况,因为处理数十亿个这些对象需要足够长的时间)
这归根结底是一个时序问题——当序列化器试图反射SensorData
准备序列化器时,IMUData
中的静态构造函数还没有执行,所以它用不完整的信息准备序列化器,然后稍后静态构造函数尝试重新配置模型 - 太晚了。
如果您使用 C# 9.0,则可以通过使用模块初始值设定项而不是静态构造函数来解决此问题(如果我们假设 SensorData
和 IMUData
在同一模块中,即 可能一个安全的假设)。以下工作正常,例如:
[ProtoContract]
public class IMUData : SensorData //, ISensorData
{
[ModuleInitializer]
internal static void Init()
{
RuntimeTypeModel.Default.Add(typeof(Quaternion), false)
.Add("W")
.Add("X")
.Add("Y")
.Add("Z");
}
请注意,如果您不使用 .NET 5(目前为预览版),您可以自己定义必要的属性:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
internal sealed class ModuleInitializerAttribute : Attribute { }
}
如果这不是一个选项,您可以简单地将模型配置代码 放在您的应用程序中更早的位置 - 最好是在启动期间,这样它就会发生 long 在序列化程序尝试开始构建模型之前。
背景信息
我有一个 .Net Standard 2.0 class 库,它使用 Protobuf-net.grpc 的代码优先方法来定义 gRPC 服务。在这个定义中,我有 classes 定义了我们用来记录传感器数据并使用 protobuf-net 将其序列化的不同数据结构。我的程序正在摄取数十万个大型对象/秒(很快就会扩展到数百万个)并且旨在用于嵌入式环境。
问题
在我下面的 class 中,我想加入一个 System.Numerics.Quaterion 作为成员。我似乎无法将其序列化。使用静态构造函数,RuntimeTypeModel 会抛出异常,因为在执行静态构造函数时已经以某种方式创建了四元数模型。因为这是一个 class 库,我非常想避免在使用 gRPC 服务的每个不同程序中调用 RuntimeTypeModel。我希望找到一种方法来序列化 System.Numerics.Quaternion.
我曾尝试将此静态构造函数运行时定义置于 class 层次结构的最高级别,但无济于事。仍然抛出异常。
[ProtoContract]
public class IMUData : SensorData, ISensorData
{
static IMUData()
{
RuntimeTypeModel.Default.Add(typeof(Quaternion), false)
.Add("W")
.Add("X")
.Add("Y")
.Add("Z");
}
... //Other members
[ProtoMember(8)]
public Quaternion Orientation
{
get; set;
}
... //Other methods and members
}
问题
我想做的事情是否可行,或者我应该简单地创建自己的四元数 class 并定义隐式运算符? (我宁愿避免这种情况,因为处理数十亿个这些对象需要足够长的时间)
这归根结底是一个时序问题——当序列化器试图反射SensorData
准备序列化器时,IMUData
中的静态构造函数还没有执行,所以它用不完整的信息准备序列化器,然后稍后静态构造函数尝试重新配置模型 - 太晚了。
如果您使用 C# 9.0,则可以通过使用模块初始值设定项而不是静态构造函数来解决此问题(如果我们假设 SensorData
和 IMUData
在同一模块中,即 可能一个安全的假设)。以下工作正常,例如:
[ProtoContract]
public class IMUData : SensorData //, ISensorData
{
[ModuleInitializer]
internal static void Init()
{
RuntimeTypeModel.Default.Add(typeof(Quaternion), false)
.Add("W")
.Add("X")
.Add("Y")
.Add("Z");
}
请注意,如果您不使用 .NET 5(目前为预览版),您可以自己定义必要的属性:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
internal sealed class ModuleInitializerAttribute : Attribute { }
}
如果这不是一个选项,您可以简单地将模型配置代码 放在您的应用程序中更早的位置 - 最好是在启动期间,这样它就会发生 long 在序列化程序尝试开始构建模型之前。