Adding 'OldValue' property to Dapper.Snapshotter.Changes object
up vote
1
down vote
favorite
We use Dapper's Snapshotter extensively so identify property changes to make updates more efficient. We're now looking to use it to identify changes that could be used for logging. In order to do this, we need to add the property OldValue
to the nested class Changes
(which has Name
and NewValue
).
All the information is there in this class, but it uses the Emit library. I've tried various line additions trying to access that original property's value and set it to OldValue
:
e.g.
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
However, I keep getting errors saying that it destabilizes the runtime. I like to tinker, but the Emit library is very new ground. I was hoping someone (...https://stackoverflow.com/users/23354/marc-gravell...https://stackoverflow.com/users/13249/nick-craver) with expertise could guide me here.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed change
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
// [original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val]
/*****
MAYBE SET ORIGINAL PROP VAL HERE?
*****/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
dapper dapper-extensions dapper-rainbow
add a comment |
up vote
1
down vote
favorite
We use Dapper's Snapshotter extensively so identify property changes to make updates more efficient. We're now looking to use it to identify changes that could be used for logging. In order to do this, we need to add the property OldValue
to the nested class Changes
(which has Name
and NewValue
).
All the information is there in this class, but it uses the Emit library. I've tried various line additions trying to access that original property's value and set it to OldValue
:
e.g.
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
However, I keep getting errors saying that it destabilizes the runtime. I like to tinker, but the Emit library is very new ground. I was hoping someone (...https://stackoverflow.com/users/23354/marc-gravell...https://stackoverflow.com/users/13249/nick-craver) with expertise could guide me here.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed change
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
// [original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val]
/*****
MAYBE SET ORIGINAL PROP VAL HERE?
*****/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
dapper dapper-extensions dapper-rainbow
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
We use Dapper's Snapshotter extensively so identify property changes to make updates more efficient. We're now looking to use it to identify changes that could be used for logging. In order to do this, we need to add the property OldValue
to the nested class Changes
(which has Name
and NewValue
).
All the information is there in this class, but it uses the Emit library. I've tried various line additions trying to access that original property's value and set it to OldValue
:
e.g.
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
However, I keep getting errors saying that it destabilizes the runtime. I like to tinker, but the Emit library is very new ground. I was hoping someone (...https://stackoverflow.com/users/23354/marc-gravell...https://stackoverflow.com/users/13249/nick-craver) with expertise could guide me here.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed change
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
// [original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val]
/*****
MAYBE SET ORIGINAL PROP VAL HERE?
*****/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
dapper dapper-extensions dapper-rainbow
We use Dapper's Snapshotter extensively so identify property changes to make updates more efficient. We're now looking to use it to identify changes that could be used for logging. In order to do this, we need to add the property OldValue
to the nested class Changes
(which has Name
and NewValue
).
All the information is there in this class, but it uses the Emit library. I've tried various line additions trying to access that original property's value and set it to OldValue
:
e.g.
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
However, I keep getting errors saying that it destabilizes the runtime. I like to tinker, but the Emit library is very new ground. I was hoping someone (...https://stackoverflow.com/users/23354/marc-gravell...https://stackoverflow.com/users/13249/nick-craver) with expertise could guide me here.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed change
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
// [original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val]
/*****
MAYBE SET ORIGINAL PROP VAL HERE?
*****/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
dapper dapper-extensions dapper-rainbow
dapper dapper-extensions dapper-rainbow
edited Nov 9 at 12:40
asked Nov 8 at 19:35
BlackjacketMack
3,6712024
3,6712024
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
1
down vote
This does it! After adding the OldValue to Changes, Basically, declare a new local variable and push retrieve the value and pop it into that local var.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed new value
il.DeclareLocal(typeof(object)); // RM - boxed old value
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
//[original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
//[original prop val]
/*
* RM - We're going to dupe and store the old value into loc3.
*/
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_3);
// [original prop val, current prop val]
/*
*
*/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
/*
* Begin setting value
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_3);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
//
/*
* End Playground
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
This does it! After adding the OldValue to Changes, Basically, declare a new local variable and push retrieve the value and pop it into that local var.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed new value
il.DeclareLocal(typeof(object)); // RM - boxed old value
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
//[original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
//[original prop val]
/*
* RM - We're going to dupe and store the old value into loc3.
*/
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_3);
// [original prop val, current prop val]
/*
*
*/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
/*
* Begin setting value
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_3);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
//
/*
* End Playground
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
add a comment |
up vote
1
down vote
This does it! After adding the OldValue to Changes, Basically, declare a new local variable and push retrieve the value and pop it into that local var.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed new value
il.DeclareLocal(typeof(object)); // RM - boxed old value
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
//[original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
//[original prop val]
/*
* RM - We're going to dupe and store the old value into loc3.
*/
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_3);
// [original prop val, current prop val]
/*
*
*/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
/*
* Begin setting value
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_3);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
//
/*
* End Playground
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
add a comment |
up vote
1
down vote
up vote
1
down vote
This does it! After adding the OldValue to Changes, Basically, declare a new local variable and push retrieve the value and pop it into that local var.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed new value
il.DeclareLocal(typeof(object)); // RM - boxed old value
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
//[original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
//[original prop val]
/*
* RM - We're going to dupe and store the old value into loc3.
*/
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_3);
// [original prop val, current prop val]
/*
*
*/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
/*
* Begin setting value
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_3);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
//
/*
* End Playground
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
This does it! After adding the OldValue to Changes, Basically, declare a new local variable and push retrieve the value and pop it into that local var.
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed new value
il.DeclareLocal(typeof(object)); // RM - boxed old value
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//
il.Emit(OpCodes.Ldarg_0);
//[original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
//[original prop val]
/*
* RM - We're going to dupe and store the old value into loc3.
*/
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_3);
// [original prop val, current prop val]
/*
*
*/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
//
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
//
/*
* Begin setting value
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_3);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
//
/*
* End Playground
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
//
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
//
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}
answered Nov 9 at 14:48
BlackjacketMack
3,6712024
3,6712024
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53214934%2fadding-oldvalue-property-to-dapper-snapshotter-changes-object%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown