Need to serialize/deserialize dataset using protobuf.net in c#

1k Views Asked by At

I am using code like this to define protobuf-net schema at runtime. And i am getting error in:

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

as "Value cannot be null". Please help me to resolve this.

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);
}
1

There are 1 best solutions below

6
On

Ultimately, the problem comes from here:

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

This will only work when oField.DataType is int, since the only single-parameter ProtoContractAttribute constructor is the one that takes int tag. For any other oField.DataType, this GetConstructor call will return null. An immediate fix would be to use typeof(int) each time there, rather than oField.DataType.

However, frankly I don't think that meta-programming is the right approach for you to use in this scenario (although it is hard to say without a complete example). If you already have an object model, many things can be configured using the RuntimeTypeModel / MetaType / ValueMember APIs. I don't know how well that would apply to your specific scenario.

If you are specifically looking at serializing a DataSet, then the "right" approach might depend on whether it is a "typed" data-set, vs a non-typed data-set. Frankly, the first thing I would say is:

stop using data-sets

but I acknowledge that there are some (limited) scenarios where they are genuinely useful. They shouldn't be your default data access technology. If you have to use data-sets (DataSet), note that there already is an optimized serialization format built in - it just isn't enabled by default. If you have tested DataSet using inbuilt .NET serialization and found it unsatisfying, try setting:

yourDataSet.RemotingFormat = SerializationFormat.Binary;

and re-run your test. This is much more efficient than the default xml format, and might be enough to avoid having to convince DataSet to play-nicely with an additional serializer.