iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 18
4

接著進到Dapper的另一個關鍵功能 : 「Parameter 參數化」

主要邏輯 :
GetCacheInfo檢查是否緩存內有動態方法 > 假如沒有緩存,使用CreateParamInfoGenerator方法Emit IL建立AddParameter動態方法 > 建立完後保存在緩存內

接著重點來看CreateParamInfoGenerator方法內的底成邏輯跟「精美細節處理」,使用了結果反推代碼方法,忽略「沒使用的欄位」不生成對應IL代碼,避免資源浪費情況。這也是前面緩存算法要去判斷不同SQL字串的原因。

以下是我挑出的源碼重點部分 :

internal static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused, IList<LiteralToken> literals)
{
	//...略
	if (filterParams)
	{
		props = FilterParameters(props, identity.sql);
	}

	var callOpCode = isStruct ? OpCodes.Call : OpCodes.Callvirt;
	foreach (var prop in props)
	{
		//Emit IL動作
	}
	//...略
}


private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyInfo> parameters, string sql)
{
	var list = new List<PropertyInfo>(16);
	foreach (var p in parameters)
	{
		if (Regex.IsMatch(sql, @"[?@:]" + p.Name + @"([^\p{L}\p{N}_]+|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant))
			list.Add(p);
	}
	return list;
}

接著查看IL來驗證,查詢代碼如下

var result = connection.Query("select @Name name ", new { Name = "暐翰", Age = 26}).First();

CreateParamInfoGenerator AddParameter 動態方法IL代碼如下 :

IL_0000: ldarg.1    
IL_0001: castclass  <>f__AnonymousType1`2[System.String,System.Int32]
IL_0006: stloc.0    
IL_0007: ldarg.0    
IL_0008: callvirt   System.Data.IDataParameterCollection get_Parameters()/System.Data.IDbCommand
IL_000d: dup        
IL_000e: ldarg.0    
IL_000f: callvirt   System.Data.IDbDataParameter CreateParameter()/System.Data.IDbCommand
IL_0014: dup        
IL_0015: ldstr      "Name"
IL_001a: callvirt   Void set_ParameterName(System.String)/System.Data.IDataParameter
IL_001f: dup        
IL_0020: ldc.i4.s   16
IL_0022: callvirt   Void set_DbType(System.Data.DbType)/System.Data.IDataParameter
IL_0027: dup        
IL_0028: ldc.i4.1   
IL_0029: callvirt   Void set_Direction(System.Data.ParameterDirection)/System.Data.IDataParameter
IL_002e: dup        
IL_002f: ldloc.0    
IL_0030: callvirt   System.String get_Name()/<>f__AnonymousType1`2[System.String,System.Int32]
IL_0035: dup        
IL_0036: brtrue.s   IL_0042
IL_0038: pop        
IL_0039: ldsfld     System.DBNull Value/System.DBNull
IL_003e: ldc.i4.0   
IL_003f: stloc.1    
IL_0040: br.s       IL_005a
IL_0042: dup        
IL_0043: callvirt   Int32 get_Length()/System.String
IL_0048: ldc.i4     4000
IL_004d: cgt        
IL_004f: brtrue.s   IL_0058
IL_0051: ldc.i4     4000
IL_0056: br.s       IL_0059
IL_0058: ldc.i4.m1  
IL_0059: stloc.1    
IL_005a: callvirt   Void set_Value(System.Object)/System.Data.IDataParameter
IL_005f: ldloc.1    
IL_0060: brfalse.s  IL_0069
IL_0062: dup        
IL_0063: ldloc.1    
IL_0064: callvirt   Void set_Size(Int32)/System.Data.IDbDataParameter
IL_0069: callvirt   Int32 Add(System.Object)/System.Collections.IList
IL_006e: pop        
IL_006f: pop        
IL_0070: ret            

IL轉成對應C#代碼:

public class TestType
{
	public static void TestMeThod(IDataReader P_0, object P_1)
	{
		var anon = (<>f__AnonymousType1<string, int>)P_1;
		IDataParameterCollection parameters = ((IDbCommand)P_0).Parameters;
		IDbDataParameter dbDataParameter = ((IDbCommand)P_0).CreateParameter();
		dbDataParameter.ParameterName = "Name";
		dbDataParameter.DbType = DbType.String;
		dbDataParameter.Direction = ParameterDirection.Input;
		object obj = anon.Name;
		int num;
		if (obj == null)
		{
			obj = DBNull.Value;
			num = 0;
		}
		else
		{
			num = ((((string)obj).Length > 4000) ? (-1) : 4000);
		}
		dbDataParameter.Value = obj;
		if (num != 0)
		{
			dbDataParameter.Size = num;
		}
		parameters.Add(dbDataParameter);
	}
}

可以發現雖然傳遞Age參數,但是SQL字串沒有用到,Dapper不會去生成該欄位的SetParameter動作IL。這個細節處理真的要給Dapper一個讚!


上一篇
【深入Dapper.NET源碼】 CommandBehavior的細節處理
下一篇
【深入Dapper.NET源碼】 IN 多集合參數化底層原理
系列文
🌊 進階學習 ADO.NET、Dapper、Entity Framework 30

尚未有邦友留言

立即登入留言