Emit学习(4) - Dapper解析之数据对象映射(一)

时间:2023-03-09 05:51:29
Emit学习(4) - Dapper解析之数据对象映射(一)

感觉好久没有写博客了, 这几天有点小忙, 接下来会更忙, 索性就先写一篇吧. 后面估计会有更长的一段时间不会更新博客了.

废话不多说, 先上菜.

一、示例

1. 先建类, 类的名称与读取的表名并没有什么关系,可以不一样, 然后就是其中的属性大小写不限

public class Tch_Teacher
{
public int Id { get; set; } public string Name { get; set; } public bool IsDoublePosition { get; set; } public DateTime CreateDate { get; set; }
} public class Test : Tch_Teacher //, ISupportInitialize 此接口有两个方法, BeginInit, EndInit
{
//[ExplicitConstructor] Dapper会优先查找设置了此属性的构造函数
//public Test() { } public string BId { get; set; } //public void BeginInit()
//{
// Console.WriteLine("Test BeginInit");
//} //public void EndInit()
//{
// Console.WriteLine("Test EndInit");
//}
}

2. 测试代码

    class Program
{
static void Main(string[] args)
{
var conStr = ConfigurationManager.ConnectionStrings["ConStr"].ToString();
using (IDbConnection conn = new MySqlConnection(conStr))
{
var sql = "select Count(1) from tch_teacher where id>@Id limit 3;";
//Console.WriteLine(conn.Query<int?>(sql, new { Id = 10 })); //error
Console.WriteLine(conn.Query<int>(sql, new { Id = }).FirstOrDefault()); //
Console.WriteLine(conn.Query<string>(sql, new { Id = }).FirstOrDefault()); // sql = "select Id, BId, No, Name, CreateDate from tch_teacher limit 3;"; //No这个字段, 在类中并没有
var list = conn.Query<Test>(sql);
Console.WriteLine(list.ToList().FirstOrDefault().BId); // c5f5959e-0744-42cd-a843-145e28149d9b
}
Console.ReadKey();
}
}
   

接下来, 可以进入Dapper的部分了

三、Dapper 开始

Query<int/string> 和 Query<Test>在读取数据的部分是一样的, 开始出现不同的地方主要体现在 object to model 的部分,

private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)

首先注意一个地方
static SqlMapper()
{
        //这部分是 简单类型处理用到的, 当然这其中并不仅仅只有简单类型
typeMap = new Dictionary<Type, DbType>();
typeMap[typeof(byte)] = DbType.Byte;
typeMap[typeof(sbyte)] = DbType.SByte;
typeMap[typeof(short)] = DbType.Int16;
typeMap[typeof(ushort)] = DbType.UInt16;
typeMap[typeof(int)] = DbType.Int32;
typeMap[typeof(uint)] = DbType.UInt32;
typeMap[typeof(long)] = DbType.Int64;
typeMap[typeof(ulong)] = DbType.UInt64;
typeMap[typeof(float)] = DbType.Single;
typeMap[typeof(double)] = DbType.Double;
typeMap[typeof(decimal)] = DbType.Decimal;
typeMap[typeof(bool)] = DbType.Boolean;
typeMap[typeof(string)] = DbType.String;
typeMap[typeof(char)] = DbType.StringFixedLength;
typeMap[typeof(Guid)] = DbType.Guid;
typeMap[typeof(DateTime)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
typeMap[typeof(TimeSpan)] = DbType.Time;
typeMap[typeof(byte[])] = DbType.Binary;
typeMap[typeof(byte?)] = DbType.Byte;
typeMap[typeof(sbyte?)] = DbType.SByte;
typeMap[typeof(short?)] = DbType.Int16;
typeMap[typeof(ushort?)] = DbType.UInt16;
typeMap[typeof(int?)] = DbType.Int32;
typeMap[typeof(uint?)] = DbType.UInt32;
typeMap[typeof(long?)] = DbType.Int64;
typeMap[typeof(ulong?)] = DbType.UInt64;
typeMap[typeof(float?)] = DbType.Single;
typeMap[typeof(double?)] = DbType.Double;
typeMap[typeof(decimal?)] = DbType.Decimal;
typeMap[typeof(bool?)] = DbType.Boolean;
typeMap[typeof(char?)] = DbType.StringFixedLength;
typeMap[typeof(Guid?)] = DbType.Guid;
typeMap[typeof(DateTime?)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
typeMap[typeof(TimeSpan?)] = DbType.Time;
typeMap[typeof(object)] = DbType.Object;
        
        //这个方法可以实现自定义处理, 它是一个public static 方法            
AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), false);
}

然后看GetDeserializer方法

private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
{ // dynamic is passed in as Object ... by c# design
if (type == typeof(object)
|| type == typeof(DapperRow))
{
          //object / dynamic 类型, 会执行以下方法
return GetDapperRowDeserializer(reader, startBound, length, returnNullIfFirstMissing);
} Type underlyingType = null;
if (!(typeMap.ContainsKey(type) || type.IsEnum || type.FullName == LinqBinary ||
(type.IsValueType && (underlyingType = Nullable.GetUnderlyingType(type)) != null && underlyingType.IsEnum)))
{
ITypeHandler handler;
if (typeHandlers.TryGetValue(type, out handler))
{
//自定义处理
return GetHandlerDeserializer(handler, type, startBound);
}
//复杂类型的处理
return GetTypeDeserializer(type, reader, startBound, length, returnNullIfFirstMissing);
}
//以上简单类型, 值类型, 可空值类型, 枚举, linq的二进制 的处理
return GetStructDeserializer(type, underlyingType ?? type, startBound);
}

这里我只介绍 复杂类型的处理方式了, 至于其他的, 跟Emit的主题关系不是很大, 有兴趣的童鞋, 可以自己去看一下, 应该是能看懂的

由于 GetTypeDeserializer 这个方法实在是太长了, 我把说明都写在注释里面去吧.  按照我的注释, 应该是能看懂整个过程的. 可能还是IL那一段不太好懂, 我第一次看的时候, 就看到那里就没继续看下去了, 实在是不想继续看了. 以下是代码部分

 /// <summary>
/// Internal use only
/// </summary>
/// <param name="type"></param>
/// <param name="reader"></param>
/// <param name="startBound"></param>
/// <param name="length"></param>
/// <param name="returnNullIfFirstMissing"></param>
/// <returns></returns>
public static Func<IDataReader, object> GetTypeDeserializer(
#if CSHARP30
Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing
#else
Type type, IDataReader reader, int startBound = , int length = -, bool returnNullIfFirstMissing = false
#endif
)
{
//创建动态方法 Deserialize[Guid]
var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), typeof(object), new[] { typeof(IDataReader) }, true);
var il = dm.GetILGenerator();
il.DeclareLocal(typeof(int)); //定义本地变量 loc0
il.DeclareLocal(type); //定义本地变量 loc1 -> target
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc_0); //初始化本地变量loc0, loc0 = 0 if (length == -)
{
length = reader.FieldCount - startBound; //获取要转换字段的个数
} if (reader.FieldCount <= startBound)
{
throw MultiMapException(reader);
} //获取读取出来的字段名, 并转入数组中 -> string[] Id, BId, No, Name, CreateDate
var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray(); ITypeMap typeMap = GetTypeMap(type); //new DefaultTypeMap(type) int index = startBound; //有参构造函数
ConstructorInfo specializedConstructor = null;
//需要初始化标志
bool supportInitialize = false;
if (type.IsValueType) //target是值类型
{
il.Emit(OpCodes.Ldloca_S, (byte)); //加载loc1的地址
il.Emit(OpCodes.Initobj, type); //初始化loc1, loc1 = 0
}
else //target是引用类型
{
var types = new Type[length];
for (int i = startBound; i < startBound + length; i++)
{
//获取读到的db值的类型
types[i - startBound] = reader.GetFieldType(i);
}
//查找标记了ExplicitConstructor属性(Attribute)的构造函数
var explicitConstr = typeMap.FindExplicitConstructor();
if (explicitConstr != null)
{
#region 存在
var structLocals = new Dictionary<Type, LocalBuilder>(); var consPs = explicitConstr.GetParameters(); //获取该构造函数上的参数集 #region 遍历加载参数
foreach (var p in consPs)
{
//引用类型
if (!p.ParameterType.IsValueType)
{
//如果传入参数为复杂类型, 则以 null 来处理
il.Emit(OpCodes.Ldnull);
}
else //值类型
{
LocalBuilder loc;
if (!structLocals.TryGetValue(p.ParameterType, out loc))
{
//定义本地变量
structLocals[p.ParameterType] = loc = il.DeclareLocal(p.ParameterType);
} il.Emit(OpCodes.Ldloca, (short)loc.LocalIndex);
il.Emit(OpCodes.Initobj, p.ParameterType); //初始化传入参数, a=0,b=false之类的
il.Emit(OpCodes.Ldloca, (short)loc.LocalIndex);
il.Emit(OpCodes.Ldobj, p.ParameterType); //加载初始化后的参数
}
}
#endregion il.Emit(OpCodes.Newobj, explicitConstr); //创建对象 new target(...);
il.Emit(OpCodes.Stloc_1); //loc1 = target //target 是否实现 ISupportInitialize 接口, 如果实现, 则调用其 BeginInit 方法
supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("BeginInit"), null);
}
#endregion
}
else
{
#region 不存在
var ctor = typeMap.FindConstructor(names, types); //查找构造函数, 优先返回无参构造函数
if (ctor == null)
{
//找不到能用的构造函数
string proposedTypes = "(" + string.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")";
throw new InvalidOperationException(string.Format("A parameterless default constructor or one matching signature {0} is required for {1} materialization", proposedTypes, type.FullName));
} if (ctor.GetParameters().Length == )
{
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_1); //loc1 = new target();
supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("BeginInit"), null);
}
}
else
{
specializedConstructor = ctor;
}
#endregion
}
} //try 开始
il.BeginExceptionBlock();
if (type.IsValueType)
{
//如果是值类型, 加载target的地址
il.Emit(OpCodes.Ldloca_S, (byte));// [target]
}
else if (specializedConstructor == null) //构造函数为无参构造函数
{
//引用类型, 则直接使用变量即可
il.Emit(OpCodes.Ldloc_1);// [target]
} //用reader中的列去匹配target中的属性, 匹配不上, 则显示为null, 此处的No为null
var members = (specializedConstructor != null
? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n))
: names.Select(n => typeMap.GetMember(n))).ToList(); //无参 // stack is now [target] bool first = true;
var allDone = il.DefineLabel();
int enumDeclareLocal = -,
//定义第二个本地变量,object类型的, 然后返回此本地变量的index值, 其实就是截止目前, 定义了本地变量的个数
valueCopyLocal = il.DeclareLocal(typeof(object)).LocalIndex;
foreach (var item in members)
{
if (item != null)
{
#region object to model if (specializedConstructor == null) //无参构造函数存在
il.Emit(OpCodes.Dup); // stack is now [target][target] Label isDbNullLabel = il.DefineLabel();
Label finishLabel = il.DefineLabel(); il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader]
EmitInt32(il, index); // stack is now [target][target][reader][index]
il.Emit(OpCodes.Dup);// stack is now [target][target][reader][index][index]
il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index] //loc0 = [index]
//获取reader读取的值, reader[index]
il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object]
il.Emit(OpCodes.Dup); // stack is now [target][target][value-as-object][value-as-object]
StoreLocal(il, valueCopyLocal); //将 reader[index]的值, 存放到本地变量 loc_valueCopyLocal 中 Type colType = reader.GetFieldType(index); //reader[index] 的列的类型 source
Type memberType = item.MemberType; //target[item] 的类型 target //如果目标类型为char 或者 char? , 则调用ReadChar / ReadNullableChar方法来完成转换
if (memberType == typeof(char) || memberType == typeof(char?))
{
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(
memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value]
}
else
{
il.Emit(OpCodes.Dup); // stack is now [target][target][value-as-object][value-as-object]
//判断是否为DBNull类型, 如果是, 则跳转到 标签isDbNullLabel
il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null]
il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object] // unbox nullable enums as the primitive, i.e. byte etc
// int? -> int, int/string -> null, 根据可空值类型来获取其值类型
var nullUnderlyingType = Nullable.GetUnderlyingType(memberType);
var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType; if (unboxType.IsEnum)
{
Type numericType = Enum.GetUnderlyingType(unboxType);
if (colType == typeof(string))
{
if (enumDeclareLocal == -)
{
enumDeclareLocal = il.DeclareLocal(typeof(string)).LocalIndex;
}
il.Emit(OpCodes.Castclass, typeof(string)); // stack is now [target][target][string]
StoreLocal(il, enumDeclareLocal); // stack is now [target][target]
il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token]
il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type]
LoadLocal(il, enumDeclareLocal); // stack is now [target][target][enum-type][string]
il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true]
il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object]
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
}
else
{
FlexibleConvertBoxedFromHeadOfStack(il, colType, unboxType, numericType);
} if (nullUnderlyingType != null)
{
il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]
}
}
else if (memberType.FullName == LinqBinary)
{
il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][target][byte-array]
il.Emit(OpCodes.Newobj, memberType.GetConstructor(new Type[] { typeof(byte[]) }));// stack is now [target][target][binary]
}
else
{
TypeCode dataTypeCode = Type.GetTypeCode(colType),
unboxTypeCode = Type.GetTypeCode(unboxType);
bool hasTypeHandler;
if ((hasTypeHandler = typeHandlers.ContainsKey(unboxType)) || colType == unboxType || dataTypeCode == unboxTypeCode || dataTypeCode == Type.GetTypeCode(nullUnderlyingType))
{
//判断是否有自定义的转换方法, 如果有, 则调用自定义的方法完成转换
if (hasTypeHandler)
{
#pragma warning disable 618
il.EmitCall(OpCodes.Call, typeof(TypeHandlerCache<>).MakeGenericType(unboxType).GetMethod("Parse"), null); // stack is now [target][target][typed-value]
#pragma warning restore 618
}
else
{
//将指令中指定类型的已装箱的表示形式转换成未装箱形式
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
}
}
else
{
// not a direct match; need to tweak the unbox
FlexibleConvertBoxedFromHeadOfStack(il, colType, nullUnderlyingType ?? unboxType, null);
if (nullUnderlyingType != null)
{
il.Emit(OpCodes.Newobj, unboxType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]
}
}
}
}
if (specializedConstructor == null)
{
// Store the value in the property/field
if (item.Property != null)
{
if (type.IsValueType)
{
il.Emit(OpCodes.Call, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]
}
else
{
il.Emit(OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]
}
}
else
{
il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
}
} il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target] il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]
if (specializedConstructor != null)
{
il.Emit(OpCodes.Pop);
if (item.MemberType.IsValueType)
{
int localIndex = il.DeclareLocal(item.MemberType).LocalIndex;
LoadLocalAddress(il, localIndex);
il.Emit(OpCodes.Initobj, item.MemberType);
LoadLocal(il, localIndex);
}
else
{
il.Emit(OpCodes.Ldnull);
}
}
else
{
il.Emit(OpCodes.Pop); // stack is now [target][target]
il.Emit(OpCodes.Pop); // stack is now [target]
} if (first && returnNullIfFirstMissing)
{
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldnull); // stack is now [null]
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Br, allDone);
} il.MarkLabel(finishLabel);
#endregion
} first = false;
index += ;
}
if (type.IsValueType)
{
il.Emit(OpCodes.Pop);
}
else
{
//构造函数为有参的构造函数
if (specializedConstructor != null)
{
//创建对象
il.Emit(OpCodes.Newobj, specializedConstructor);
}
il.Emit(OpCodes.Stloc_1); // stack is empty //实现 ISupportInitialize 接口, 调用 EndInit 方法, 完成初始化
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("EndInit"), null);
}
}
il.MarkLabel(allDone);
//try 结束 -> catch 开始
il.BeginCatchBlock(typeof(Exception)); // stack is Exception
il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader
LoadLocal(il, valueCopyLocal); // stack is Exception, index, reader, value
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ThrowDataException"), null); //抛出异常
il.EndExceptionBlock();
//catch 结束 il.Emit(OpCodes.Ldloc_1); // stack is [rval] 此处就是转换后的最终结果
if (type.IsValueType)
{
il.Emit(OpCodes.Box, type);
}
il.Emit(OpCodes.Ret); return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader, object>));
}

其中的value-as-object是从reader中读取出来的未转换的数据, typed-value是转换后的数据

本想做成可收缩的, 但是那种展开后, 不能点, 只要一点, 就自动收起来了, 感觉不方便, 所以还是贴出来了

其中还有些地方不够详细, 不过对照着这个, 去看Dapper源码, 是可以看的懂了

在下一篇中, 我会画出堆栈中的变化, 来减少理解难度