iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 8
2
Software Development

🌊 進階學習 ADO.NET、Dapper、Entity Framework 系列 第 8

【深入Dapper.NET源碼】Strongly Typed Mapping 原理 Part5 : Emit IL反建立C#代碼

有了前面Expression版本概念後,接著可以進到Dapper底層最核心的技術 : Emit。

首先要有個概念,MSIL(CIL)目的是給JIT編譯器看的,所以可讀性會很差、難Debug,但比起Expression來說可以做到更細節的邏輯操作。

在實際環境開發使用Emit,一般會先寫好C#代碼後 > 反編譯查看IL > 使用Emit建立動態方法,舉例 :

1.首先建立一個簡單打印例子 :

void SyaHello()
{
	Console.WriteLine("Hello World");	
}

2.反編譯查看IL

SyaHello:
IL_0000:  nop         
IL_0001:  ldstr       "Hello World"
IL_0006:  call        System.Console.WriteLine
IL_000B:  nop         
IL_000C:  ret  

3.使用DynamicMethod + Emit建立動態方法

void Main()
{
	// 1. 建立 void 方法()
	DynamicMethod methodbuilder = new DynamicMethod("Deserialize" + Guid.NewGuid().ToString(),typeof(void),null);

	// 2. 建立方法Body內容,藉由Emit
	var il = methodbuilder.GetILGenerator();
	il.Emit(OpCodes.Ldstr, "Hello World");
	Type[] types = new Type[1]
	{
		typeof(string)
	};
	MethodInfo method = typeof(Console).GetMethod("WriteLine", types);
	il.Emit(OpCodes.Call,method);
	il.Emit(OpCodes.Ret);
	
	// 3. 轉換指定類型的Func or Action
	var action = (Action)methodbuilder.CreateDelegate(typeof(Action));
	
	action(); 
}

https://ithelp.ithome.com.tw/upload/images/20190924/20105988bD9GSXyjNt.png

但是對已經寫好的專案來說就不是這樣流程了,開發者不一定會好心的告訴你當初設計的邏輯,所以接著討論此問題。

如果像是Dapper只有Emit IL沒有C# Source Code專案怎麼辦?

我的解決方式是 : 「既然只有Runtime才能知道IL,那麼將IL保存成靜態檔案再反編譯查看」

這邊可以使用MethodBuild + Save方法將IL保存成靜態exe檔案 > 反編譯查看,但需要特別注意

  1. 請對應好參數跟返回類別,否則會編譯錯誤。
  2. netstandard不支援此方式,Dapper需要使用region if 指定版本來做區分,否則不能使用,如圖片
    20191004230125.png

代碼如下 :

	//使用MethodBuilder查看別人已經寫好的Emit IL
	//1. 建立MethodBuilder
	AppDomain ad = AppDomain.CurrentDomain;
	AssemblyName am = new AssemblyName();
	am.Name = "TestAsm";
	AssemblyBuilder ab = ad.DefineDynamicAssembly(am, AssemblyBuilderAccess.Save);
	ModuleBuilder mb = ab.DefineDynamicModule("Testmod", "TestAsm.exe");
	TypeBuilder tb = mb.DefineType("TestType", TypeAttributes.Public);
	MethodBuilder dm = tb.DefineMethod("TestMeThod", MethodAttributes.Public |
	MethodAttributes.Static, type, new[] { typeof(IDataReader) });
	ab.SetEntryPoint(dm);

	// 2. 填入IL代碼
	//..略

	// 3. 生成靜態檔案
	tb.CreateType();
	ab.Save("TestAsm.exe");

接著使用此方式在GetTypeDeserializerImpl方法反編譯Dapper Query Mapping IL,可以得出C#代碼 :

public static User TestMeThod(IDataReader P_0)
{
	int index = 0;
	User user = new User();
	object value = default(object);
	try
	{
		User user2 = user;
		index = 0;
		object obj = value = P_0[0];
		if (!(obj is DBNull))
		{
			user2.Name = (string)obj;
		}
		index = 1;
		object obj2 = value = P_0[1];
		if (!(obj2 is DBNull))
		{
			user2.Age = (int)obj2;
		}
		user = user2;
		return user;
	}
	catch (Exception ex)
	{
		SqlMapper.ThrowDataException(ex, index, P_0, value);
		return user;
	}
}

20191004230548.png

有了C#代碼後再來了解Emit邏輯會快很多,接著就可以進到Emit版本Query實作部分。


上一篇
【深入Dapper.NET源碼】Strongly Typed Mapping 原理 Part4 : Expression版本
下一篇
【深入Dapper.NET源碼】Strongly Typed Mapping 原理 Part6 : Emit版本
系列文
🌊 進階學習 ADO.NET、Dapper、Entity Framework 30

尚未有邦友留言

立即登入留言