Overview (forgive me for being so detailed, but I would prefer it to be too much than too little): I'm trying to edit the Dapper source for our solution so that when reading any DateTime or Nullable from the database, the property DateTime.Kind always has a DateTimeKind.Utc value.
In our system, all DateTimes coming from the front are guaranteed in UTC, and the database (Sql Server Azure) stores them as a DateTime type in UTC (we do not use DateTimeOffsets, we just make sure that DateTime is UTC before storing him in the database.)
I read all about how to generate code for DynamicMethods using ILGenerator.Emit (...), and I feel that I have a decent understanding of how it works with the evaluation stack, locals, etc. In my efforts to solve this problem, I wrote small code samples that will help me bring to the ultimate goal. I wrote DynamicMethod to take DateTime as an argument, call DateTime.SpecifyKind, return a value. Same thing with DateTime? type using its Nullable.Value property to get the DateTime for the SpecifyKind method.
This is where my problem arises: in a dapper DateTime (or DateTime? I really don't know, but when I handle it as if it either doesn't work out what I expect), in a box. Therefore, when I try to use OpCodes.Unbox or OpCodes.Unbox_Any, then treat the result as DateTime or DateTime ?, I get VerificationException: the operation can destabilize the runtime.
Obviously, I'm missing something important in boxing, but I'll give you my code samples and maybe you can help me get it working.
It works:
[Test]
public void Reflection_Emit_Test3()
{
var dm = new DynamicMethod("SetUtc", typeof(DateTime?), new Type[] {typeof(DateTime?)});
var nullableType = typeof(DateTime?);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarga_S, 0);
il.Emit(OpCodes.Call, nullableType.GetProperty("Value").GetGetMethod());
il.Emit(OpCodes.Ldc_I4, (int)DateTimeKind.Utc);
il.Emit(OpCodes.Call, typeof(DateTime).GetMethod("SpecifyKind"));
il.Emit(OpCodes.Newobj, nullableType.GetConstructor(new[] {typeof (DateTime)}));
il.Emit(OpCodes.Ret);
var meth = (Func<DateTime?, DateTime?>)dm.CreateDelegate(typeof(Func<DateTime?, DateTime?>));
DateTime? now = DateTime.Now;
Assert.That(now.Value.Kind, Is.Not.EqualTo(DateTimeKind.Utc));
var nowUtc = meth(now);
Assert.That(nowUtc.Value.Kind, Is.EqualTo(DateTimeKind.Utc));
}
I get what I expect here. Hurrah! But that is not the end, because we have unboxing to deal with ...
[Test]
public void Reflection_Emit_Test4()
{
var dm = new DynamicMethod("SetUtc", typeof(DateTime?), new Type[] { typeof(object) });
var nullableType = typeof(DateTime?);
var il = dm.GetILGenerator();
il.DeclareLocal(typeof (DateTime?));
il.Emit(OpCodes.Ldarga_S, 0);
il.Emit(OpCodes.Unbox_Any, typeof(DateTime?));
il.Emit(OpCodes.Call, nullableType.GetProperty("Value").GetGetMethod());
il.Emit(OpCodes.Ldc_I4, (int)DateTimeKind.Utc);
il.Emit(OpCodes.Call, typeof(DateTime).GetMethod("SpecifyKind"));
il.Emit(OpCodes.Newobj, nullableType.GetConstructor(new[] { typeof(DateTime) }));
il.Emit(OpCodes.Ret);
var meth = (Func<object, DateTime?>)dm.CreateDelegate(typeof(Func<object, DateTime?>));
object now = new DateTime?(DateTime.Now);
Assert.That(((DateTime?) now).Value.Kind, Is.Not.EqualTo(DateTimeKind.Utc));
var nowUtc = meth(now);
Assert.That(nowUtc.Value.Kind, Is.EqualTo(DateTimeKind.Utc));
}
It just doesn't work. I get a VerificationException and then I cry in the corner for a while until I am ready to try again.
DateTime DateTime? ( unbox , DateTime eval, DateTime?), .
-, , , ?