【问题标题】:Need to serialize/deserialize dataset using protobuf.net in c#需要在 c# 中使用 protobuf.net 序列化/反序列化数据集
【发布时间】:2018-01-06 13:45:30
【问题描述】:

我正在使用这样的代码在运行时定义 protobuf-net 架构。而且我遇到了错误:

CustomAttributeBuilder contractMem = new CustomAttributeBuilder(
    contractMemInfoCon, new object[] { index });

因为“值不能为空”。请帮我解决这个问题。

AssemblyName oAssemblyName = new AssemblyName();
oAssemblyName.Name = "TEST";
AssemblyBuilder oAssmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly("Test", AssemblyBuilderAccess.Run);
ModuleBuilder oModule = oAssmBuilder.DefineDynamicModule("TestModule.Module");


TypeBuilder oTypeBuilder = oModule.DefineType("TestType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);

ConstructorBuilder constructor = oTypeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

//For Defining protocontract                   
ConstructorInfo contractInfoCon = typeof(ProtoBuf.ProtoContractAttribute).GetConstructor(new Type[0]);

CustomAttributeBuilder cab = new CustomAttributeBuilder(contractInfoCon,  new object[0]);

oTypeBuilder.SetCustomAttribute(cab);
string sDataType = "", sPropertyName = "";
int index = 0;
//oFields contains SP columns
foreach (Types.Field oField in oFields)
{
    sPropertyName = oField.ID;
    sDataType = oField.DataType;
    index = index + 1;

    FieldBuilder field = oTypeBuilder.DefineField(sPropertyName, oField.DataType, FieldAttributes.Public);

    PropertyBuilder property =
    oTypeBuilder.DefineProperty("_" + sPropertyName,
             System.Reflection.PropertyAttributes.None,
             oField.DataType,
             new Type[] { oField.DataType });

    MethodAttributes GetSetAttr = MethodAttributes.Public |     MethodAttributes.HideBySig;

    MethodBuilder currGetPropMthdBldr =
    oTypeBuilder.DefineMethod("get_value",
                   GetSetAttr,
                   oField.DataType,
                   Type.EmptyTypes);

    ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
    currGetIL.Emit(OpCodes.Ldarg_0);
    currGetIL.Emit(OpCodes.Ldfld, field);
    currGetIL.Emit(OpCodes.Ret);

    MethodBuilder currSetPropMthdBldr =
    oTypeBuilder.DefineMethod("set_value",
                   GetSetAttr,
                   null,
                   new Type[] { oField.DataType });

    ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
    currSetIL.Emit(OpCodes.Ldarg_0);
    currSetIL.Emit(OpCodes.Ldarg_1);
    currSetIL.Emit(OpCodes.Stfld, field);
    currSetIL.Emit(OpCodes.Ret);

    property.SetGetMethod(currGetPropMthdBldr);
    property.SetSetMethod(currSetPropMthdBldr);

    ConstructorInfo contractMemInfoCon = typeof(ProtoBuf.ProtoMemberAttribute).GetConstructor(new [] { oField.DataType });
    CustomAttributeBuilder contractMem = new CustomAttributeBuilder(contractMemInfoCon, new object[] { index });
    property.SetCustomAttribute(contractMem);
}

【问题讨论】:

  • 请尝试edit您的问题,为您的问题添加一些细节。因为它是我们不能告诉你到目前为止到底尝试了什么,什么不起作用。事实上,minimal reproducible example 构建样本 DataSet 将是理想的。就目前而言,这个问题不太可能得到回答。请参阅How to Askcodeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question,了解如何提出更有可能得到回答的问题。说了这么多,你可以从Serializing a dataTable using protobuf开始。
  • 另外,尝试更全面地标记未来的问题。你在用protobuf-net吗?还是Google.Protobuf?还是别的什么?
  • 嗨; protobuf-net 作者在这里;我可以问:你为什么在这里使用 TypeBuilder?您试图通过元编程实现什么目标? protobuf-net 中的大多数东西都可以通过RuntimeTypeModel API 在运行时配置 - 我强烈怀疑这里的 IL 东西会分散您实际尝试做的事情的注意力。那么:您真正想做的事情是什么
  • 数据集是邪恶的......只用于简单的东西,绝对不能用于任何分布式......
  • 你找到解决了吗?我同样需要将数据集来回发送到 gRPC 服务。

标签: c# serialization protobuf-net typebuilder


【解决方案1】:

归根结底,问题来自这里:

ConstructorInfo contractMemInfoCon = typeof(ProtoBuf.ProtoMemberAttribute).GetConstructor(
    new [] { oField.DataType });

这仅在oField.DataTypeint 时有效,因为唯一的单参数ProtoContractAttribute 构造函数是采用int tag 的构造函数。对于任何其他oField.DataType,此GetConstructor 调用将返回null。一个直接的解决方法是每次都使用typeof(int),而不是oField.DataType

但是,坦率地说,我不认为元编程是您在这种情况下使用的正确方法(尽管没有完整的示例很难说)。如果您已经有一个对象模型,则可以使用RuntimeTypeModel / MetaType / ValueMember API 配置许多东西。我不知道这将如何适用于您的特定场景。

如果您专门研究序列化DataSet,那么“正确”的方法可能取决于它是“类型化”数据集还是非类型化数据集。坦率地说,我要说的第一件事是:

停止使用数据集

但我承认有一些(有限的)场景真正有用。它们不应该是您的默认数据访问技术。如果您使用数据集 (DataSet),请注意已经有一种优化的序列化格式内置 - 它只是默认情况下不启用。如果您使用内置 .NET 序列化测试了 DataSet 并发现它不满意,请尝试设置:

yourDataSet.RemotingFormat = SerializationFormat.Binary;

然后重新运行您的测试。这比默认的 xml 格式要高效得多,并且可能足以避免不得不说服 DataSet 与额外的序列化程序一起玩得很好。

【讨论】:

  • 谢谢 marc ,但在我的情况下,最初我使用二进制格式化程序来序列化/反序列化数据集,为了提高性能(例如:从数据库中输出 100 万条记录),我切换到 protobuf.net,其中我使用ProtoBuf.Serializer.Serialize(stream, oDataSet); 序列化数据集,它工作正常。我怎样才能反序列化它。请帮我解决这个问题。
  • @Mathy BinaryFormatter 使用 RemotingFormat 设置;您至少应该将其考虑在内并使用第二种内置格式对其进行测试。重新 protobuf-net 和数据集,我非常怀疑“它工作正常”。如果它当前没有反序列化,你怎么可能这么说?
  • @Mathy 如果没有具体的可运行示例,很难发表更多评论。我认为您的 emit 方法是一英里宽,并且从一开始就做错了事情,但即使这样也不能让我运行任何东西来查看您所看到的。
  • 同意标记最糟糕的项目,这些项目在生产后推出使用分布式数据集时会出现巨大的复杂问题。由于数据表的内部结构,Proto(或任何非 Xml)+ 数据集是一个坏主意。
  • @Mathy 你能分享你的代码吗?我需要将 WCF 迁移到 gRPC,并且所有服务都使用 c# 数据集,并且无法将其更改为模型,因为它是一个遗留系统。谢谢
猜你喜欢
  • 1970-01-01
  • 2014-01-15
  • 2018-08-05
  • 1970-01-01
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 2016-05-18
  • 2011-08-26
相关资源
最近更新 更多