I have to define a method with Reflection.Emit that is rather complex, because I have to do a for loop on a field and have a condition with break and return. My method that I want to recreate with reflection looks like this in regular code:
override int GetKeyImpl(Type obj0)
{
int answer = -1;
for(int i = 0; i < knownTypes.length; i++){
if(knowntypes[i] == obj0){
answer = i;
break;
}
}
return answer;
}
My idea to solve this problem was to generate a method with reflection that redirects the call to my original method and returns the int.
I need to know how to do a for loop and breaks with OpCodes to recreate the method while doing conditional checks on an array that is inside a class. I've searched for tutorials but didn't find any that go further than addition of two ints.
Edit: Forgot to mention it, I'm using IKVM.Reflection and knownTypes is an array of Type[]. The method that im writing is one that will override an abstract one.
This should reproduce the method you specified:
TypeBuilder type = /* ... */;
FieldInfo knownFields = /* ... */;
// Finding dependencies via reflection
var baseMethod = type.BaseType.GetMethod(
"GetKeyImpl",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var typeEqualsOperator = typeof(Type).GetMethod(
"op_Equality",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
null,
new[] { typeof(Type), typeof(Type) },
null);
// Declaring the method
var getKeyImpl = type.DefineMethod(
baseMethod.Name,
baseMethod.Attributes & ~(MethodAttributes.Abstract |
MethodAttributes.NewSlot));
// Setting return type
getKeyImpl.SetReturnType(typeof(int));
// Adding parameters
getKeyImpl.SetParameters(typeof(Type));
getKeyImpl.DefineParameter(1, ParameterAttributes.None, "obj0");
// Override the base method
type.DefineMethodOverride(getKeyImpl, baseMethod);
var il = getKeyImpl.GetILGenerator();
// Preparing locals
var answer = il.DeclareLocal(typeof(int));
var i = il.DeclareLocal(typeof(int));
// Preparing labels
var loopCondition = il.DefineLabel();
var loopIterator = il.DefineLabel();
var returnLabel = il.DefineLabel();
var loopBody = il.DefineLabel();
// Writing body
// answer = -1
il.Emit(OpCodes.Ldc_I4_M1);
il.Emit(OpCodes.Stloc, answer);
// i = 0
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc, i);
// jump to loop condition
il.Emit(OpCodes.Br_S, loopCondition);
// begin loop body
il.MarkLabel(loopBody);
// if (obj0 != knownTypes[i]) continue
il.Emit(OpCodes.Ldarg_0); // omit if 'knownTypes' is static
il.Emit(OpCodes.Ldfld, knownTypes); // use 'Ldsfld' if 'knownTypes' is static
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Ldarg_1); // use 'Ldarg_0' if 'knownTypes' is static
il.Emit(OpCodes.Call, typeEqualsOperator);
il.Emit(OpCodes.Brfalse_S, loopIterator);
// answer = i; jump to return
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Stloc, answer);
il.Emit(OpCodes.Br_S, returnLabel);
// begin loop iterator
il.MarkLabel(loopIterator);
// i = i + 1
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Stloc, i);
// begin loop condition
il.MarkLabel(loopCondition);
// if (i < knownTypes.Length) jump to loop body
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Ldarg_0); // omit if 'knownTypes' is static
il.Emit(OpCodes.Ldfld, knownTypes); // use 'Ldsfld' if 'knownTypes' is static
il.Emit(OpCodes.Ldlen);
il.Emit(OpCodes.Conv_I4);
il.Emit(OpCodes.Blt_S, loopBody);
// return answer
il.MarkLabel(returnLabel);
il.Emit(OpCodes.Ldloc, answer);
il.Emit(OpCodes.Ret);
// Finished!
The decompiled results are as expected:
override int GetKeyImpl(Type obj0)
{
for (int i = 0; i < this.knownTypes.Length; i++)
{
if (this.knownTypes[i] == obj0)
return i;
}
return -1;
}
If you have access to .NET Reflector, there is a Reflection.Emit Language Add-In that may interest you. Alternatively, write a prototype in C# code, and then run it through a disassembler to see the raw IL.
If it had been okay to make the method static (and accept knownTypes as a parameter or make it a static field), then you could have composed the method body using LINQ expression trees. Unfortunately, you cannot compose instance method bodies using this technique; they have to be static. Example:
var method = typeBuilder.DefineMethod(
"GetKeyImpl",
MethodAttributes.Private |
MethodAttributes.Static |
MethodAttributes.HideBySig);
var type = E.Parameter(typeof(Type), "type");
var knownTypes = E.Parameter(typeof(Type[]), "knownTypes");
var answer = E.Variable(typeof(int), "answer");
var i = E.Variable(typeof(int), "i");
var breakTarget = E.Label("breakTarget");
var continueTarget = E.Label("continueTarget");
var returnTarget = E.Label(typeof(int), "returnTarget");
var forLoop = E.Block(
new[] { i },
E.Assign(i, E.Constant(0)),
E.Loop(
E.Block(
E.IfThen(
E.GreaterThanOrEqual(i, E.ArrayLength(knownTypes)),
E.Break(breakTarget)),
E.IfThen(
E.Equal(E.ArrayIndex(knownTypes, i), type),
E.Return(returnTarget, i)),
E.Label(continueTarget),
E.PreIncrementAssign(i))),
E.Label(breakTarget));
var body = E.Lambda<Func<Type, Type[], int>>(
E.Block(
new[] { answer },
E.Assign(answer, E.Constant(-1)),
forLoop,
E.Label(returnTarget, answer)),
type,
knownTypes);
body.CompileToMethod(method);
return method;
The example above accepts knownTypes as the second parameter. Refactoring to read from a static field instead would be straightforward. The decompiled results, again, are as expected:
private static int GetKeyImpl(Type type, Type[] knownTypes)
{
for (int i = 0; i < knownTypes.Length; i++)
{
if (knownTypes[i] == type)
return i;
}
return -1;
}
The easist way to work out how to generate the IL for a method is to create a simple console application that has your method in. Then build it and run ILDasm against it to look at the IL instructions necessary to make up the method.
Once you can see the instructions it shouldn't be too difficult to write code to emit the OpCodes necessary.
Related
I was looking at a way to implement a class Proxy in .NET (Core) and found out there is actually an implementation in the framework called DispatchProxy (source code). When I looked at the source code, it is actually implemented here at DispatchProxyGenerator.
I am interested in knowing how it is implemented. However I reach an impasse here as my knowledge is limited. I cannot really understand how it works. Can someone enlighten me?
My best guess from the code is it try to create at runtime the type members using System.Reflection and emits some IL code, is it correct? Suppose I want to create a very simple DispatchProxy implementation, can I simply use something like DynamicObject and returns a delegate instead?
private void Complete()
{
Type[] args = new Type[_fields.Count];
for (int i = 0; i < args.Length; i++)
{
args[i] = _fields[i].FieldType;
}
ConstructorBuilder cb = _tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, args);
ILGenerator il = cb.GetILGenerator();
// chained ctor call
ConstructorInfo baseCtor = _proxyBaseType.GetTypeInfo().DeclaredConstructors.SingleOrDefault(c => c.IsPublic && c.GetParameters().Length == 0);
Debug.Assert(baseCtor != null);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseCtor);
// store all the fields
for (int i = 0; i < args.Length; i++)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg, i + 1);
il.Emit(OpCodes.Stfld, _fields[i]);
}
il.Emit(OpCodes.Ret);
}
I am trying to create a run-time mock, which will have a specific behaviour for every method, which will be called. For example this mock should always return null - which is already working - or raise an exception for a not reachable database or in this specific case throw an Argument Exception.
I know, that this could be done fairly easy with a mocking framework. But since I have to use this via Spring.NET, I can not use a mocking framework, as far as I know. This is because I have to give a type into the StaticApplicationContext. If there is an easier way to create mock types, let me know.
The code below will throw an Expected:
But was: (Common Language Runtime detected an invalid program.)
at TypeMock.Foo()
at TypeBuilderTest.
public interface IInterfaceWithObject
{
String Foo();
}
[Test]
public void MinimalExample()
{
//build expression
var constructorInfo = typeof(ArgumentNullException).GetConstructor(new Type[0]);
Assert.IsNotNull(constructorInfo);
Expression expression = Expression.Throw(Expression.New(constructorInfo));
//create type
// based upon https://stackoverflow.com/questions/38345486/dynamically-create-a-class-by-interface
var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
var mb = ab.DefineDynamicModule("Test");
TypeBuilder typeBuilder = mb.DefineType("TypeMock");
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
typeBuilder.AddInterfaceImplementation(typeof(IInterfaceWithObject));
List<MethodInfo> methods = new List<MethodInfo>(typeof(IInterfaceWithObject).GetMethods());
foreach (Type interf in typeof(IInterfaceWithObject).GetInterfaces())
{
foreach (MethodInfo method in interf.GetMethods())
if (!methods.Contains(method))
methods.Add(method);
}
foreach (var imethod in methods)
{
var parameters = imethod.GetParameters();
List<Type> parameterTypes = new List<Type>();
foreach (var parameter in parameters)
{
parameterTypes.Add(parameter.ParameterType);
}
var method =
typeBuilder.DefineMethod
(
"##" + imethod.Name,
MethodAttributes.Public | MethodAttributes.Static,
imethod.ReturnType,
parameterTypes.ToArray()
);
var thisParameter = Expression.Parameter(typeof(IInterfaceWithObject), "this");
var bodyExpression = Expression.Lambda
(expression
,
thisParameter
);
bodyExpression.CompileToMethod(method);
var stub =
typeBuilder.DefineMethod(imethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, imethod.ReturnType, parameterTypes.ToArray());
var il = stub.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, null);
il.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(stub, imethod);
}
Type type = typeBuilder.CreateType();
//create instance via spring.net
StaticApplicationContext context = new StaticApplicationContext();
MutablePropertyValues value = new MutablePropertyValues();
string objectName = "Integer";
context.RegisterSingleton(objectName, type, value);
var objectInstance = (IInterfaceWithObject)context.GetObject(objectName);
Assert.Throws<ArgumentNullException>(() => objectInstance.Foo());
}
I'm learning IL and I thought of writing kind of a high-performance hack to access a field values of any object (like a reflection but faster).
So I made this class for testing:
public class CrashTestDummy
{
public int Number { get; set; }
public CrashTestDummy(int number)
{
Number = number;
}
public override string ToString()
{
return string.Format("CrashTestDummy: Number = {0}", Number);
}
}
Then I have such a program (I added comments after all IL instructions to improve readability, also divided into several logical parts; after every part there is written what I think is now on the stack):
class Program
{
static void Main(string[] args)
{
var backingFieldFormat = "<{0}>k__BackingField";
var getPropFormat = "get_{0}";
var dummy = new CrashTestDummy(5);
var t = dummy.GetType();
var f = t.GetField(string.Format(backingFieldFormat, "Number"),
BindingFlags.Instance | BindingFlags.NonPublic);
// define method: object Getter(Type, FieldInfo, Object), ignoring private fields.
var getter = new DynamicMethod("Getter", typeof(object), new Type[] { typeof(Type), typeof(FieldInfo), typeof(object) }, true);
var il = getter.GetILGenerator();
var _t = il.DeclareLocal(typeof(Type)); // Type _t;
var _f = il.DeclareLocal(typeof(FieldInfo)); // FieldInfo _f;
var _ft = il.DeclareLocal(typeof(Type)); // Type _ft;
var get_FieldType = typeof(FieldInfo).GetMethod(string.Format(getPropFormat, "FieldType")); // MethodInfo for FieldInfo.FieldType getter
var get_IsValueType = typeof(Type).GetMethod(string.Format(getPropFormat, "IsValueType")); // MethodInfo for Type.IsValueType getter
var lbl_NotValueType = il.DefineLabel(); // label "NotValueType"
// PART 1.
il.Emit(OpCodes.Ldarg_0); // Get argument 0 (type of object) ...
il.Emit(OpCodes.Castclass, typeof(Type)); // ... cast it to Type (just in case) ...
il.Emit(OpCodes.Stloc, _t); // ... and assign it to _t.
il.Emit(OpCodes.Ldarg_1); // Get argument 1 (desired field of object) ...
il.Emit(OpCodes.Castclass, typeof(FieldInfo)); // ... cast it to FieldInfo (just in case) ...
il.Emit(OpCodes.Stloc, _f); // ... and assign it to _f.
// stack is empty
// DEBUG PART
il.EmitWriteLine(_t); // these two lines show that both
il.EmitWriteLine(t.ToString()); // t and _t contains equal Type
il.EmitWriteLine(_f); // these two lines show that both
il.EmitWriteLine(f.ToString()); // f and _f contains equal FieldInfo
// stack is empty
// PART 2.
il.Emit(OpCodes.Ldarg_2); // Get argument 2 (object itself) ...
il.Emit(OpCodes.Castclass, _t); // ... cast it to type of object ...
il.Emit(OpCodes.Ldfld, _f); // ... and get it's desired field's value.
// desired field's value on the stack
// PART 3.
il.Emit(OpCodes.Ldloc, _f); // Get FieldInfo ...
il.Emit(OpCodes.Call, get_FieldType); // ... .FieldType ...
il.Emit(OpCodes.Call, get_IsValueType); // ... .IsValueType; ...
il.Emit(OpCodes.Brfalse, lbl_NotValueType); // ... IF it's false - goto NotValueType.
il.Emit(OpCodes.Ldloc, _f); // Get FieldInfo ...
il.Emit(OpCodes.Call, get_FieldType); // ... .FieldType ...
il.Emit(OpCodes.Stloc, _ft); // ... and assign it to _ft.
il.Emit(OpCodes.Box, _ft); // Box field's value of type _ft.
il.MarkLabel(lbl_NotValueType); // NotValueType:
// desired field's value on the stack (boxed, if it's value type)
// PART 4.
il.Emit(OpCodes.Ret); // return.
var value = getter.Invoke(null, new object[] { t, f, dummy });
Console.WriteLine(value);
Console.ReadKey();
}
}
This code crashes (on Invoke, and Exception from within Emit is as helpful as always). I can replace PARTs 2. and 3. as below:
// ALTERNATE PART 2.
il.Emit(OpCodes.Ldarg_2); // Get argument 2 (object itself) ...
il.Emit(OpCodes.Castclass, t); // ... cast it to type of object ...
il.Emit(OpCodes.Ldfld, f); // ... and get it's desired field's value.
// desired field's value on the stack
// ALTERNATE PART 3.
if (f.FieldType.IsValueType)
il.Emit(OpCodes.Box, f.FieldType); // Box field's value of type f.FieldType.
// desired field's value on the stack (boxed, if it's value type)
and it works fine. Notice that this time I'm not using any local variables, f and t are variables from outside of method. However with this approach I would need to generate as many methods, as number of types and fields it was being used to. So it's pretty unsatisfying solution.
I'm doing something wrong with local variables, apparently, but I was unable to figure out what it is exactly. What am I missing?
Edit:
Here's the code after big simplification. CrashTestDummy has now string property, so I could get rid of boxing the int:
public class CrashTestDummy
{
public string Text { get; set; }
public CrashTestDummy(string text)
{
Text = text;
}
}
And the main code is as follows:
static string BackingField(string propertyName)
{
return string.Format("<{0}>k__BackingField", propertyName);
}
static void Main(string[] args)
{
// INIT
var dummy = new CrashTestDummy("Loremipsum");
var t = typeof(CrashTestDummy);
var f = t.GetField(BackingField("Text"),
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
var fieldGetter = new DynamicMethod("FieldGetter", typeof(object), new Type[] { typeof(object) }, true);
var il = fieldGetter.GetILGenerator();
// DYNAMIC METHOD CODE
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, t);
il.Emit(OpCodes.Ldfld, f);
il.Emit(OpCodes.Ret);
var d = (Func<object, object>)fieldGetter.CreateDelegate(typeof(Func<object, object>));
// BENCHMARK
Stopwatch sw = new Stopwatch();
var len = 1000000;
for (int i = 0; i < len; i++)
{
sw.Start();
d(dummy);
sw.Stop();
}
Console.WriteLine(sw.Elapsed);
sw.Reset();
for (int i = 0; i < len; i++)
{
sw.Start();
f.GetValue(dummy);
sw.Stop();
}
Console.WriteLine(sw.Elapsed);
Console.ReadKey();
}
What you could do is create a specialized accessor function on the first use of any given FieldInfo. This removes reflection costs for subsequent accesses and replaces them with the cost of a delegate call which is much cheaper.
It's only about 2x faster than [reflection]
I would doubt this benchmark result. How could it be faster? If you generate the same code in IL that the C# compiler would have generated you will get the same level of performance. No point in doing that.
I am currently serializing SQL table rows into a binary format for efficient storage. I serialize/deserialize the binary data into a List<object> per row. I'm trying to upgrade this to use POCOs, that will be dynamically generated (emitted) with one Field per column.
I've been searching online for hours and have stumbled upon ORMs/frameworks like EF, T4, ExpandoObject, but all of these either use a dynamic object (properties can be added/removed on the fly) or simply generate a POCO before compiling. I cannot use templating because the schema of the tables is unknown at compile time, and using dynamic objects would be overkill (and slow) since I know the exact set of properties and their types. I need to generate one POCO per table, with Fields corresponding to columns, and with the data types set accordingly (INT -> int, TEXT -> string).
After generating the POCO, I'll proceed to get/set properties using emitted CIL, much like what PetaPoco does for statically compiled POCOs. I'm hoping all of this rigmarole will be faster than using untyped Lists, and give me high-fidelity POCOs that are strongly-typed and can be accelerated by the CLR. Am I correct to assume this? and can you start me off on generating POCOs at runtime? And will using POCOs be much faster or much more memory-efficient than using a List<object>? Basically, will it be worth the trouble? I already know how to accelerate getting/setting Fields using emitted CIL.
From comments and chat, it seems that a key part of this is still creating a dynamic type; ok, here's a full example that shows a fully serializable (by any common serializer) type. You could of course add more to the type - maybe indexers to get properties by number or by name, INotifyPropertyChanged, etc.
Also - critical point: you must cache and re-use the generated Type instances. Do not keep regenerating this stuff... you will 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") };
for (int i = 0; i < 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); // this
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); // this
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] { i + 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 => 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];
for (int i = 0; i < fields.Length; i++)
{
branches[i] = il.DefineLabel();
}
il.Emit(OpCodes.Ldarg_1); // key
il.Emit(OpCodes.Switch, branches); // switch
// default:
il.ThrowException(typeof(ArgumentOutOfRangeException));
for (int i = 0; i < fields.Length; i++)
{
il.MarkLabel(branches[i]);
il.Emit(OpCodes.Ldarg_0); // this
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 => 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];
for (int i = 0; i < fields.Length; i++)
{
branches[i] = il.DefineLabel();
}
il.Emit(OpCodes.Ldarg_1); // key
il.Emit(OpCodes.Switch, branches); // switch
// default:
il.ThrowException(typeof(ArgumentOutOfRangeException));
for (int i = 0; i < fields.Length; i++)
{
il.MarkLabel(branches[i]);
il.Emit(OpCodes.Ldarg_0); // this
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 the index (via a known interface) to set the values
IBasicRecord rec = (IBasicRecord)obj;
rec[0] = 123;
rec[1] = "abc";
rec[2] = 12F;
rec[3] = DateTime.Now;
for (int i = 0; i < 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 do this unless you have a custom Binder; your
// assembly WILL NOT deserialize in the next AppDomain (i.e.
// the next time you load your app, you won't be able to load)
// - shown only for 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
This is actually quite a complex question. Unfortunately, to answer it fully you would have to basically write it and test it, however - I strongly suggest not looking at any on-the-fly POCO generation until you have your answer! Basically, you should ignore that step for now.
The other essential question in performance is: how fast does it need to be? The absolute first thing I would do is the absolutely simplest thing that works, and measure that. And the simplest thing that works is: load it into a DataTable and serialize that datatable (using RemotingFormat = RemotingFormat.Binary;). In 10 lines of code that will give you a
line in the sand:
var dt = new DataTable();
dt.Load(yourDataReader);
//... any access tests
dt.RemotingFormat = SerializationFormat.Binary;
using (var file = File.Create(path))
{
var bf = new BinaryFormatter();
bf.Serialize(file, dt);
}
// ... also check deserialize, if that is perf-critical
Normally I wouldn't recommend either DataTable or BinaryFormatter, but... it doesn't seem far-fetched in this case.
Personally, I suspect you'll find that DataTable in binary-remoting-mode isn't actually terrible.
The next step is to see what else works without any huge effort. For example:
loading a data-source into objects is a solved problem, with tools like dapper
serializing a set of objects in a very efficient way is a solved problem, with tools like protobuf-net
So I would be tempted to create an illustrative class (purely to see if it is any better) along the lines of:
[DataContract]
public class Foo {
[DataMember(Order=1)] public int Id {get;set;}
[DataMember(Order=2)] public string Name {get;set;}
// ... more props
// IMPORTANT: make this representative - basically, the same data
// that you had in the data-table
// note also include any supporting info - any indexers and interface
// support that your core code needs
}
[DataContract]
public class FooWrapper { // just to help in the test
[DataMember(Order=1)] public List<Foo> Items {get;set;}
}
and do the same test (your main code would only use the indexer access, but let dapper use the .Query<Foo>(...) API for now):
var data = conn.Query<Foo>(...).ToList(); // dapper
//... any access tests, just using the indexer API
using (var file = File.Create(path))
{
var wrapper = new FooWrapper { Items = data };
Serializer.Serialize(file, wrapper); // protobuf-net
}
// note that you deserialize via Serializer.Deserialize<FooWrapper>(file)
The point of this is that this will give you some bounds on what is reasonable to expect in terms of what can be achieved. Feel free to use your own materializer/serializer in place of dapper/protobuf-net, but I humbly submit that these two have been heavily optimized for scenarios largely like this.
When you have a lower and upper bound, you have sensible data to answer the "is it worth it" question. Generating objects at run-time isn't massively hard, but it is more work than most people would need to do. You also want to be really careful to re-use the generated types as far as possible. Note that if you go that route, protobuf-net has a fully non-generic API, via Serializer.NonGeneric or RuntimeTypeModel.Default (all three options end up at the same core). Dapper doesn't, but I would be more than happy to add one (accepting a Type instance). In the interim, you could also use MakeGenericMethod / Invoke for that one step.
I realize I haven't directly answered "is it worth it", but that is deliberate: that cannot be answered without direct application to your scenario. Hopefully, I have instead provided some hints at how you can answer it for your scenario. I would be very interested in hearing your findings.
Only when you know that it is worth it (and with the above I would expect that to take about an hour's effort) would I go to the trouble of generating types. If you do, I recommend the use of Sigil - it will make your IL generation far less frustrating.
the question is : if i have MyConvertDataRowToEntity(DataRow row )
and I call in with T object from type Parent and inside I call the same function with desendant type Child how should I pass the DataRow parameter ?
The problem is created when Invoke of MakeGenericMethod called.
Did change the type to DataSet , string and String types .
No luck.
(I recognize the children object bu prefix in column names - PrefixDataColumn )
public static T MyConvertDataRowToEntity<T>(DataRow row ) where T : class, new()
{
Type objType = typeof(T);
Type parentObjType = typeof(T);
T obj = Activator.CreateInstance<T>(); //hence the new() contsraint
PropertyInfo propertyGenericType = null;
object childInstance = null;
PropertyInfo property;
string childColumnName = string.Empty ;
foreach (DataColumn column in row.Table.Columns)
{
column.ColumnName = column.ColumnName.Replace("_", "");
string PrefixDataColumn;
if (column.ColumnName.IndexOf(".") > (-1))
{
///gets the prefix that is the same as child entity name
PrefixDataColumn = column.ColumnName.Substring(0, column.ColumnName.IndexOf("."));
///the column name in the child
int length = column.ColumnName.Length - 1;
int start = column.ColumnName.IndexOf(".") + 1;
childColumnName = column.ColumnName.Substring(column.ColumnName.IndexOf(".") + 1);
propertyGenericType = objType.GetProperty(PrefixDataColumn,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
parentObjType = objType;
if (propertyGenericType != null)
{
Type childType = propertyGenericType.PropertyType;
objType = childType;
childInstance = Activator.CreateInstance(propertyGenericType.PropertyType);
// get the get method for the property
MethodInfo method = propertyGenericType.GetGetMethod(true);
// get the generic get-method generator
MethodInfo genericHelper = typeof(DataUtil).GetMethod("MyConvertDataRowToEntity", BindingFlags.Public | BindingFlags.Static);
List<Type> signature = new List<Type>();
// first type parameter is type of target object
signature.Add(childType);
//next parameters are real types of method arguments
foreach (ParameterInfo pi in genericHelper.GetParameters())
{
signature.Add(pi.ParameterType);
}
// last parameters are known types of method arguments
signature.AddRange(typeof(T).GetGenericArguments());
// reflection call to the generic get-method generator to generate the type arguments
//MethodInfo constructedHelper = genericHelper.MakeGenericMethod(signature.ToArray());
// reflection call to the generic get-method generator to generate the type arguments
MethodInfo constructedHelper = genericHelper.MakeGenericMethod(childType );
// now call it. The null argument is because it's a static method.
object ret = constructedHelper.Invoke(null, new object[] { method });
// object myObj = method.Invoke(null, row);
//// property.SetValue(obj, MyConvertDataRowToEntity<childInstance>(DataRow row),null);
// childInstance = DataUtil.GetMethod("MyConvertDataRowToEntity").MakeGenericMethod(childType); //MyConvertDataRowToEntity<object>(row);
//childType initializedChild = ;
//property.SetValue(obj, value, null);
//objType = parentObjType;
}
else
{
continue;
}
}
}
return obj;
}
Getting this error :
Object of type 'System.Reflection.RuntimeMethodInfo' cannot be converted to type 'System.Data.DataRow'.
Is there any solution for this ?
p.s.
Narrowed down the code as much as i could.
How can I invoke the Method recursivly with desedant types and pass datarow ?
The reason for the error is because you are calling MyConvertDataRowToEntity<ChildType> but then passing in the getaccessor methodinfo for the property as the parameter instead of a data row containing only those fields.
If you want to continue with the code processing logic you are currently using you would need to construct a new datarow containing the fields you wanted (with the prefix and ".") removed from the start of the column names.
Alternatively you could create a helper method the accepted a column name, the source object and it simply updated the value.
static void UpdateItemProperty<T>(T item, string columnName, object rowValue) {
var prefixColumn=columnName.IndexOf(".")==-1 ? columnName : columnName.Split(".")[0];
var pi = typeof(T).GetProperty(prefixColumn // Add your binding flags);
// if pi==null then there is an error...
if (column.ColumnName.IndexOf(".") == (-1)) { // No Nesting
pi.SetValue(item,rowValue);
return;
}
// Nesting
var child=pi.GetValue(item);
if (child==null) {
// Logic here to get childs type and create an instance then call pi.SetValue with child
}
var remainder=string.Join(',',columnName.Split(".").Skip(1).ToArray());
// make your generic method info for UpdateItemProperty with pi.PropertyType into mi
mi.Invoke(null,new object[] { child,remainder,value };
}