c# - Fast serialization and deserialization using dynamically emitted POCOs -


i serializing sql table rows binary format efficient storage. serialize/deserialize binary data list<object> per row. i'm trying upgrade use pocos, dynamically generated (emitted) 1 field per column.

i've been searching online hours , have stumbled upon orms/frameworks ef, t4, expandoobject, of these either use dynamic object (properties can added/removed on fly) or generate poco before compiling. cannot use templating because schema of tables unknown @ compile time, , using dynamic objects overkill (and slow) since know exact set of properties , types. need generate 1 poco per table, fields corresponding columns, , data types set accordingly (int -> int, text -> string).

after generating poco, i'll proceed get/set properties using emitted cil, petapoco statically compiled pocos. i'm hoping of rigmarole faster using untyped lists, , give me high-fidelity pocos strongly-typed , can accelerated clr. correct assume this? , can start me off on generating pocos @ runtime? , using pocos faster or more memory-efficient using list<object>? basically, worth trouble? know how accelerate getting/setting fields using emitted cil.

from comments , chat, seems key part of still creating dynamic type; ok, here's full example shows serializable (by common serializer) type. of course add more type - maybe indexers properties number or name, inotifypropertychanged, etc.

also - critical point: must cache , re-use generated type instances. not keep regenerating stuff... hemorrhage memory.

using newtonsoft.json; using protobuf; using system; using system.io; using system.reflection; using system.reflection.emit; using system.runtime.serialization; using system.runtime.serialization.formatters.binary; using system.text; using system.xml.serialization;  public interface ibasicrecord {     object this[int field] { get; set; } } class program {     static void main()     {         object o = 1;         int foo = (int)o;         string[] names = { "id", "name", "size", "when" };         type[] types = { typeof(int), typeof(string), typeof(float), typeof(datetime?) };          var asm = appdomain.currentdomain.definedynamicassembly(             new assemblyname("dynamicstuff"),             assemblybuilderaccess.run);         var module = asm.definedynamicmodule("dynamicstuff");         var tb = module.definetype("mytype", typeattributes.public | typeattributes.serializable);         tb.setcustomattribute(new customattributebuilder(             typeof(datacontractattribute).getconstructor(type.emptytypes), new object[0]));         tb.addinterfaceimplementation(typeof(ibasicrecord));          fieldbuilder[] fields = new fieldbuilder[names.length];         var datamemberctor = typeof(datamemberattribute).getconstructor(type.emptytypes);         var datamemberprops = new[] { typeof(datamemberattribute).getproperty("order") };         (int = 0; < fields.length; i++)         {             var field = fields[i] = tb.definefield("_" + names[i],                 types[i], fieldattributes.private);              var prop = tb.defineproperty(names[i], propertyattributes.none,                 types[i], type.emptytypes);             var getter = tb.definemethod("get_" + names[i],                 methodattributes.public | methodattributes.hidebysig, types[i], type.emptytypes);             prop.setgetmethod(getter);             var il = getter.getilgenerator();             il.emit(opcodes.ldarg_0); //             il.emit(opcodes.ldfld, field); // .foo             il.emit(opcodes.ret); // return             var setter = tb.definemethod("set_" + names[i],                 methodattributes.public | methodattributes.hidebysig, typeof(void), new type[] { types[i] });             prop.setsetmethod(setter);             il = setter.getilgenerator();             il.emit(opcodes.ldarg_0); //             il.emit(opcodes.ldarg_1); // value             il.emit(opcodes.stfld, field); // .foo =             il.emit(opcodes.ret);              prop.setcustomattribute(new customattributebuilder(                 datamemberctor, new object[0],                 datamemberprops, new object[1] { + 1 }));         }          foreach (var prop in typeof(ibasicrecord).getproperties())         {             var accessor = prop.getgetmethod();             if (accessor != null)             {                 var args = accessor.getparameters();                 var argtypes = array.convertall(args, => a.parametertype);                 var method = tb.definemethod(accessor.name,                     accessor.attributes & ~methodattributes.abstract,                     accessor.callingconvention, accessor.returntype, argtypes);                 tb.definemethodoverride(method, accessor);                 var il = method.getilgenerator();                 if (args.length == 1 && argtypes[0] == typeof(int))                 {                     var branches = new label[fields.length];                     (int = 0; < fields.length; i++)                     {                         branches[i] = il.definelabel();                     }                     il.emit(opcodes.ldarg_1); // key                     il.emit(opcodes.switch, branches); // switch                     // default:                     il.throwexception(typeof(argumentoutofrangeexception));                     (int = 0; < fields.length; i++)                     {                         il.marklabel(branches[i]);                         il.emit(opcodes.ldarg_0); //                         il.emit(opcodes.ldfld, fields[i]); // .foo                         if (types[i].isvaluetype)                         {                             il.emit(opcodes.box, types[i]); // (object)                         }                         il.emit(opcodes.ret); // return                     }                 }                 else                 {                     il.throwexception(typeof(notimplementedexception));                 }             }             accessor = prop.getsetmethod();             if (accessor != null)             {                 var args = accessor.getparameters();                 var argtypes = array.convertall(args, => a.parametertype);                 var method = tb.definemethod(accessor.name,                     accessor.attributes & ~methodattributes.abstract,                     accessor.callingconvention, accessor.returntype, argtypes);                 tb.definemethodoverride(method, accessor);                 var il = method.getilgenerator();                 if (args.length == 2 && argtypes[0] == typeof(int) && argtypes[1] == typeof(object))                 {                     var branches = new label[fields.length];                     (int = 0; < fields.length; i++)                     {                         branches[i] = il.definelabel();                     }                     il.emit(opcodes.ldarg_1); // key                     il.emit(opcodes.switch, branches); // switch                     // default:                     il.throwexception(typeof(argumentoutofrangeexception));                     (int = 0; < fields.length; i++)                     {                         il.marklabel(branches[i]);                         il.emit(opcodes.ldarg_0); //                         il.emit(opcodes.ldarg_2); // value                         il.emit(types[i].isvaluetype ? opcodes.unbox_any : opcodes.castclass, types[i]); // (sometype)                         il.emit(opcodes.stfld, fields[i]); // .foo =                         il.emit(opcodes.ret); // return                     }                 }                 else                 {                     il.throwexception(typeof(notimplementedexception));                 }             }         }          var type = tb.createtype();         var obj = activator.createinstance(type);         // we'll use index (via known interface) set values         ibasicrecord rec = (ibasicrecord)obj;         rec[0] = 123;         rec[1] = "abc";         rec[2] = 12f;         rec[3] = datetime.now;         (int = 0; < 4; i++)         {             console.writeline("{0} = {1}", i, rec[i]);         }         using (var ms = new memorystream())         {             var ser = new xmlserializer(type);             ser.serialize(ms, obj);             console.writeline("xmlserializer: {0} bytes", ms.length);         }         using (var ms = new memorystream())         {             using (var writer = new streamwriter(ms, encoding.utf8, 1024, true))             {                 var ser = new jsonserializer();                 ser.serialize(writer, obj);             }             console.writeline("json.net: {0} bytes", ms.length);         }         using (var ms = new memorystream())         {             var ser = new datacontractserializer(type);             ser.writeobject(ms, obj);             console.writeline("datacontractserializer: {0} bytes", ms.length);         }         using (var ms = new memorystream())         {             serializer.nongeneric.serialize(ms, obj);             console.writeline("protobuf-net: {0} bytes", ms.length);         }         using (var ms = new memorystream())         {             // note: never unless have custom binder;             // assembly not deserialize in next appdomain (i.e.             // next time load app, won't able load)             // - shown illustration             var bf = new binaryformatter();             bf.serialize(ms, obj);             console.writeline("binaryformatter: {0} bytes", ms.length);         }     } } 

output:

xmlserializer: 246 bytes json.net: 81 bytes datacontractserializer: 207 bytes protobuf-net: 25 bytes binaryformatter: 182 bytes 

Comments

Popular posts from this blog

linux - xterm copying to CLIPBOARD using copy-selection causes automatic updating of CLIPBOARD upon mouse selection -

c++ - qgraphicsview horizontal scrolling always has a vertical delta -