通过反编译让SpecFlow支持多层属性值的验证

需求:在使用SpecFlow时,我希望能对目标对象所关联的对象属性进行验证,但SpecFlow(Version 1.9.0)无法实现。如图中红框,可以对专户所属的金融机构的名称进行验证。

反编译步骤

  1. 利用ildasm(.net自带)反编译 TechTalk.SpecFlow.dll 生成IL中间代码 一般会生成 *.il 和*.res 一些其它资源文件 *.resources;
  2. 然后用记事本修改IL文件;
  3. 然后用ilasm编译中间代码,生成新的 TechTalk.SpecFlow.dll 文件;
  4. 删除签名。

  详细步骤见参考文献[1]

修改前后代码对比

第一处修改

TechTalk.SpecFlow.Assist.TEHelpers 类的 IsPropertyMatchingToColumnName 方法,带“.”的列名不验证是否存在对应的属性。

// 修改前的源代码
1
internal static bool IsPropertyMatchingToColumnName(PropertyInfo property, string columnName) 2 { 3 return property.Name.MatchesThisColumnName(columnName); 4 }
 // 修改前的中间语言
1
.method assembly hidebysig static bool IsPropertyMatchingToColumnName(class [mscorlib]System.Reflection.PropertyInfo 'property', 2 string columnName) cil managed 3 { 4 // Code size 13 (0xd) 5 .maxstack 8 6 IL_0000: ldarg.0 7 IL_0001: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() 8 IL_0006: ldarg.1 9 IL_0007: call bool TechTalk.SpecFlow.Assist.TEHelpers::MatchesThisColumnName(string, 10 string) 11 IL_000c: ret 12 } // end of method TEHelpers::IsPropertyMatchingToColumnName
// 修改后的源代码
1
internal static bool IsPropertyMatchingToColumnName(PropertyInfo property, string columnName) 2 { 3 return ((columnName.IndexOf('.') > -1) || property.Name.MatchesThisColumnName(columnName)); 4 }
 // 修改后的中间语言
1
.method assembly hidebysig static bool IsPropertyMatchingToColumnName(class [mscorlib]System.Reflection.PropertyInfo 'property', 2 string columnName) cil managed 3 { 4 // Code size 40 (0x28) 5 .maxstack 2 6 .locals init (bool V_0, 7 bool V_1) 8 IL_0000: nop 9 IL_0001: ldarg.1 10 IL_0002: ldc.i4.s 46 11 IL_0004: callvirt instance int32 [mscorlib]System.String::IndexOf(char) 12 IL_0009: ldc.i4.m1 13 IL_000a: cgt 14 IL_000c: ldc.i4.0 15 IL_000d: ceq 16 IL_000f: stloc.1 17 IL_0010: ldloc.1 18 IL_0011: brtrue.s IL_0017 19 IL_0013: ldc.i4.1 20 IL_0014: stloc.0 21 IL_0015: br.s IL_0026 22 IL_0017: ldarg.0 23 IL_0018: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() 24 IL_001d: ldarg.1 25 IL_001e: call bool TechTalk.SpecFlow.Assist.TEHelpers::MatchesThisColumnName(string, 26 string) 27 IL_0023: stloc.0 28 IL_0024: br.s IL_0026 29 IL_0026: ldloc.0 30 IL_0027: ret 31 } // end of method TEHelpers::IsPropertyMatchingToColumnName

 第二处修改

TechTalk.SpecFlow.Assist.PropertyExtensionMethods 类的 GetPropertyValue 方法,将属性名根据“.”分割后,循环获取关联对线的属性值。

// 修改前的源代码
1
public static object GetPropertyValue(this object @object, string propertyName) 2 { 3 return GetThePropertyOnThisObject(@object, propertyName).GetValue(@object, null); 4 }
 // 修改前的中间语言
1
.method public hidebysig static object GetPropertyValue(object 'object', 2 string propertyName) cil managed 3 { 4 .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 5 // Code size 17 (0x11) 6 .maxstack 3 7 .locals init (class [mscorlib]System.Reflection.PropertyInfo V_0) 8 IL_0000: ldarg.0 9 IL_0001: ldarg.1 10 IL_0002: call class [mscorlib]System.Reflection.PropertyInfo TechTalk.SpecFlow.Assist.PropertyExtensionMethods::GetThePropertyOnThisObject(object, 11 string) 12 IL_0007: stloc.0 13 IL_0008: ldloc.0 14 IL_0009: ldarg.0 15 IL_000a: ldnull 16 IL_000b: callvirt instance object [mscorlib]System.Reflection.PropertyInfo::GetValue(object, 17 object[]) 18 IL_0010: ret 19 } // end of method PropertyExtensionMethods::GetPropertyValue
// 修改后的源代码
1
public static object GetPropertyValue(this object @object, string propertyName) 2 { 3 foreach (string str in propertyName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries)) 4 { 5 @object = GetThePropertyOnThisObject(@object, str).GetValue(@object, null); 6 } 7 return @object; 8 }
 // 修改后的中间语言
1
.method public hidebysig static object GetPropertyValue(object 'object', 2 string propertyName) cil managed 3 { 4 .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 5 // Code size 64 (0x40) 6 .maxstack 4 7 .locals init (string[] V_0, 8 string V_1, 9 char[] V_2, 10 string[] V_3, 11 int32 V_4) 12 IL_0000: ldarg.1 13 IL_0001: ldc.i4.1 14 IL_0002: newarr [mscorlib_6]System.Char 15 IL_0007: stloc.2 16 IL_0008: ldloc.2 17 IL_0009: ldc.i4.0 18 IL_000a: ldc.i4.s 46 19 IL_000c: stelem.i2 20 IL_000d: ldloc.2 21 IL_000e: ldc.i4.1 22 IL_000f: callvirt instance string[] [mscorlib_6]System.String::Split(char[], 23 valuetype [mscorlib_6]System.StringSplitOptions) 24 IL_0014: stloc.0 25 IL_0015: ldloc.0 26 IL_0016: stloc.3 27 IL_0017: ldc.i4.0 28 IL_0018: stloc.s V_4 29 IL_001a: br.s IL_0037 30 IL_001c: ldloc.3 31 IL_001d: ldloc.s V_4 32 IL_001f: ldelem.ref 33 IL_0020: stloc.1 34 IL_0021: ldarg.0 35 IL_0022: ldloc.1 36 IL_0023: call class [mscorlib]System.Reflection.PropertyInfo TechTalk.SpecFlow.Assist.PropertyExtensionMethods::GetThePropertyOnThisObject(object, 37 string) 38 IL_0028: ldarg.0 39 IL_0029: ldnull 40 IL_002a: callvirt instance object [mscorlib_6]System.Reflection.PropertyInfo::GetValue(object, 41 object[]) 42 IL_002f: starg.s 'object' 43 IL_0031: ldloc.s V_4 44 IL_0033: ldc.i4.1 45 IL_0034: add 46 IL_0035: stloc.s V_4 47 IL_0037: ldloc.s V_4 48 IL_0039: ldloc.3 49 IL_003a: ldlen 50 IL_003b: conv.i4 51 IL_003c: blt.s IL_001c 52 IL_003e: ldarg.0 53 IL_003f: ret 54 } // end of method PropertyExtensionMethods::GetPropertyValue

参考文献

[1] 编辑IL文件,修改DLL文件

[2] IL指令详细

原文地址:https://www.cnblogs.com/tuty/p/5716882.html