继上篇Dapper源码学习和源码修改 讲了下自己学习Dapper的心得之后,下篇也随之而来,上篇主要讲的入参解析那下篇自然主打出参映射了。
好了,废话不多说,开始吧。
学习之前你的先学习怎么使用Dapper,这个我在上篇都提过,如果没使用过Dapper的同学,先去看看怎么使用吧,我这也简单贴一部分代码吧。
使用查询的Demo
//查询
sql = "select * from Teacher";
var list = SqlMapper.Query<Teacher>(conn, sql, null).ToList();
sql = "select * from Teacher left join Student on Teacher.Id=Student.Tid";
//一对一
var list1 = SqlMapper.Query<Teacher, Student, Teacher>(conn, sql,
(t, s) =>
{
if (t.Student == null) t.Student = new List<Student>();
t.Student.Add(s);
return t;
}
, null, true, null, "Id", null, null);
//一对多
Dictionary<string, Teacher> list2Dict = new Dictionary<string, Teacher>();//这个才是最后的结果
var list2 = SqlMapper.Query<Teacher, Student, Teacher>(conn, sql,
(t, s) =>
{
Teacher temp;
if (!list2Dict.TryGetValue(t.Id, out temp))
{
temp = t;
list2Dict.Add(temp.Id, temp);
}
if (temp.Student == null) temp.Student = new List<Student>();
temp.Student.Add(s);
return temp;
}
, null, true, null, "Id", null, null);
好了,我也不解释,自己体会。
我们先看看Dapper提供了哪些对外的查询方法呢,既然是将出参,只有查询才会涉及到DataReader转实体的呢,所以主要就看那几个查询方法就行了。
前两个是一个实体映射的,后面三个是多个实体映射,正常情况下一个实体对应一个表,你也可以一个表对应多个实体也是可行的。
由浅入深,先看单个实体的查询。
private static IEnumerable<T> QueryInternal<T>(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType)
{
var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
var info = GetCacheInfo(identity);
using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType))
{
using (var reader = cmd.ExecuteReader())
{
Func<Func<IDataReader, object>> cacheDeserializer = delegate()
{
info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);
SetQueryCache(identity, info);
return info.Deserializer;
};
if (info.Deserializer == null)
{
cacheDeserializer();
}
var deserializer = info.Deserializer;
while (reader.Read())
{
object next;
try
{
next = deserializer(reader);
}
catch (DataException)
{
deserializer = cacheDeserializer();
next = deserializer(reader);
}
yield return (T)next;
}
}
}
}
这个就是单个实体的查询方法,用一张图片说明
很明显在读取reader的时候 next = deserializer(reader); 就是这个将reader转成实体的,那这个deserializer是什么呢,往上看啊,上面重点二字的地方就是创建deserializer 委托的地方,对了这里插一句这里委托Func(有返回值的泛型委托),之前在入参讲解的时候那里委托是Action(无返回值的泛型委托)。
也就是说deserializer就是创建委托的地方,我们去看看它的庐山真面目。
private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
{
Func<IDataReader, object> func = null;
if (IsSimpleValue(type))
{
func = GetSimpleDeserializer(type, startBound);
}
else if (typeof(IDictionary).IsAssignableFrom(type))
{
func = GetDictionaryDeserializer(type, startBound);
}
else if (type.IsClass)
{
func = GetClassDeserializer(type, reader, startBound, length, returnNullIfFirstMissing);
}
return func;
}
func = GetSimpleDeserializer(type, startBound); func = GetDictionaryDeserializer(type, startBound); 这两个是我扩展的两种类型,就是为了让出参支持简单类型和继承IDictionary的类型。
而 func = GetClassDeserializer(type, reader, startBound, length, returnNullIfFirstMissing); 这个才是重点中的难点,这个就是将reader转成实体的委托。
呵呵,我也不多说,这个就是Dapper核心价值所在,使用Emit创建实体。
上面不是说到,我也扩展出参支持两种类型嘛,使用的时候就是这样
其一:IDictionary
sql = "select * from student";// where Id='916a84c6-85cb-4b41-b52a-96a0685d91b5'";var sex = SqlMapper.Query<Dictionary<string, object>>(conn, sql, null).ToList();
很简单的将结果转成 Dictionary ,它的委托实现如下:
private static Func<IDataReader, object> GetDictionaryDeserializer(Type type, int index)
{
return delegate(IDataReader r)
{
IDictionary ht = Activator.CreateInstance(type) as IDictionary;
for (int i = 0; i < r.FieldCount; i++)
{
ht.Add(r.GetName(i), r[i]);
}
return ht;
};
}
其二:SimpleValue
何为SimpleValue呢,看下面
private static bool IsSimpleValue(Type type)
{
if (
type.IsEnum ||
isSame(type, typeof(byte)) || isSame(type, typeof(byte?)) ||
isSame(type, typeof(sbyte)) || isSame(type, typeof(sbyte?)) ||
isSame(type, typeof(long)) || isSame(type, typeof(long?)) ||
isSame(type, typeof(ulong)) || isSame(type, typeof(ulong?)) ||
isSame(type, typeof(short)) || isSame(type, typeof(short?)) ||
isSame(type, typeof(ushort)) || isSame(type, typeof(ushort?)) ||
isSame(type, typeof(int)) || isSame(type, typeof(int?)) ||
isSame(type, typeof(uint)) || isSame(type, typeof(uint?)) ||
isSame(type, typeof(float)) || isSame(type, typeof(float?)) ||
isSame(type, typeof(double)) || isSame(type, typeof(double?)) ||
isSame(type, typeof(decimal)) || isSame(type, typeof(decimal?)) ||
isSame(type, typeof(char)) || isSame(type, typeof(char?)) ||
isSame(type, typeof(bool)) || isSame(type, typeof(bool?)) ||
isSame(type, typeof(DateTime)) || isSame(type, typeof(DateTime?)) ||
isSame(type, typeof(string)) || isSame(type, typeof(object))
)
return true;
else
return false;
}
如果是上面的类型,我就会用下面的委托来转换reader
private static Func<IDataReader, object> GetSimpleDeserializer(Type type, int index)
{
return delegate(IDataReader r)
{
object obj = r.GetValue(index);
if (obj == null || (obj is DBNull)) return type.IsValueType ? Activator.CreateInstance(type) : null;
else
{
if (type.IsEnum)
obj = Convert.ChangeType(obj, typeof(int));
else
obj = Convert.ChangeType(obj, type);
return obj;
}
};
}
后续,上面只是讲到单个实体查询的情况,至于多个实体的查询,只需理解两个参数
public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param, bool buffered, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType)
这两个 splitOn 和 map 如果理解了这两个参数,其他跟单个实体是一样,至于这两个参数我先不讲解,后面我会提供源码,自己看,或者以后抽空我再讲讲。
总结:
出参就讲到这吧,后续我看讲不讲Dapper扩展的,会提供源码下载,源码可能和文章会有些出入,发布文章后我修改过源码。
相关文章:
Dapper源码学习和源码修改
.Net开源微型ORM框架测评
.NET Core开发:项目实践
.NET Core 使用Dapper 操作MySQL
Dapper、Entity Framework 和混合应用
原文地址:http://www.cnblogs.com/deeround/p/6633611.html
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注