IL到c#的转换

时间:2013-01-24 22:58:00

标签: c# reflection delegates il mono.cecil

我正在开发一个项目,该项目几乎没有测试方法,并且最近已经更改了大量代码。特别是代码已从使用一个数据提供程序移植到另一个数据提供程序,并且DataSet已被替换为DTO。

我现在想尽可能地对此进行测试,但是无法在所需的时间范围内为许多人所做的所有更改实际创建测试。

使用Mono.cecil我已经能够编写一个测试,它找到所有数据库调用和相关的存储过程名称,并将c#中定义的参数与数据库中的参数进行比较。

我想更进一步实际调用该方法,即使所有参数都是默认值。这样,虽然DataReader可能没有任何行,但我可以发现模式并尝试将其与静态映射器方法进行比较。

例如

var reader = cmd.ExecuteReader();
var Divisions = (from IDataRecord record in reader.GetRows() 
select RecordMapper.GetDivisionFromRecord(record)).ToList();

其中GetRows()是一个简单的扩展方法,GetDivisionFromRecord是静态方法,看起来有点像这样

public static Division GetDivisionFromRecord(record)
{
  return new Division
  {
       Id = record.GetValueOrDefault<long?>("id"),
       Name = record.GetValueOrDefault<string>("name")
  };
}

Unfortunatley我无法弄清楚如何将IL引用转换为缓存的静态委托回到mapper类中的静态方法的名称。 如果我能找到方法名称,那么我可以使用Cecil获取方法并找到GetValueOrDefault的callvirt操作码,这样我就可以构建数据库别名和类型的集合。

IL看起来像这样。

IL_0025: callvirt instance class [System.Data]System.Data.IDataReader IOracleHelper::ExecuteReader(class OracleCommandWrapper)
IL_002a: stloc.0
IL_002b: ldloc.0
IL_002c: call class [mscorlib]System.Collections.Generic.IEnumerable`1<class [System.Data]System.Data.IDataRecord> NullSafeExtensions::GetRows(class [System.Data]System.Data.IDataReader)
IL_0031: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Cast<class [System.Data]System.Data.IDataRecord>(class [mscorlib]System.Collections.IEnumerable)
IL_0036: ldsfld class [System.Core]System.Func`2<class [System.Data]System.Data.IDataRecord, class [Services.Common]Division> DataDao::'CS$<>9__CachedAnonymousMethodDelegate17'
IL_003b: brtrue.s IL_0050
IL_003d: ldnull
IL_003e: ldftn class [Services.Common]Division DataDao::'<GetAlDivisions>b__16'(class [System.Data]System.Data.IDataRecord)
IL_0044: newobj instance void class [System.Core]System.Func`2<class [System.Data]System.Data.IDataRecord, class [Services.Common]Division>::.ctor(object, native int)
IL_0049: stsfld class [System.Core]System.Func`2<class [System.Data]System.Data.IDataRecord, class [Services.Common]Division> DataDao::'CS$<>9__CachedAnonymousMethodDelegate17'
IL_004e: br.s IL_0050
IL_0050: ldsfld class [System.Core]System.Func`2<class [System.Data]System.Data.IDataRecord, class [Services.Common]Division> DataDao::'CS$<>9__CachedAnonymousMethodDelegate17'
IL_0055: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!1> [System.Core]System.Linq.Enumerable::Select<class [System.Data]System.Data.IDataRecord, class [Services.Common]Division>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Core]System.Func`2<!!0, !!1>)
IL_005a: call class [mscorlib]System.Collections.Generic.List`1<!!0> [System.Core]System.Linq.Enumerable::ToList<class [Services.Common]Division>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)

我已经使用了cecil和reflection,并且可以将CachedAnonymousDelegates视为私有静态字段。但是我可以看到如何获得实际的方法调用。

我有什么想法可以解决这个问题?

1 个答案:

答案 0 :(得分:1)

最后,我使用ICSharpCode.Decompiler中的AstBuilder和ICSharpCode.NRefactory将相关的IL从Mono.cecil转换回c#,这样我就可以使用正则表达式来发现映射器了。

使用这种方法,我使用所有这些元数据来生成应该首先完成的数百个测试。