Dapper源码学习和源码修改(下篇)
繼上篇Dapper源碼學習和源碼修改 講了下自己學習Dapper的心得之后,下篇也隨之而來,上篇主要講的入?yún)⒔馕瞿窍缕匀恢鞔虺鰠⒂成淞恕?/p>
好了,廢話不多說,開始吧。
學習之前你的先學習怎么使用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>();//這個才是最后的結(jié)果
? ? ? ? ? ? ? ? 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轉(zhuǎn)實體的呢,所以主要就看那幾個查詢方法就行了。
?
前兩個是一個實體映射的,后面三個是多個實體映射,正常情況下一個實體對應一個表,你也可以一個表對應多個實體也是可行的。
由淺入深,先看單個實體的查詢。
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轉(zhuǎn)成實體的,那這個deserializer是什么呢,往上看啊,上面重點二字的地方就是創(chuàng)建deserializer 委托的地方,對了這里插一句這里委托Func(有返回值的泛型委托),之前在入?yún)⒅v解的時候那里委托是Action(無返回值的泛型委托)。
也就是說deserializer就是創(chuàng)建委托的地方,我們?nèi)タ纯此膹]山真面目。
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轉(zhuǎn)成實體的委托。
呵呵,我也不多說,這個就是Dapper核心價值所在,使用Emit創(chuàng)建實體。
上面不是說到,我也擴展出參支持兩種類型嘛,使用的時候就是這樣
其一:IDictionary
sql = "select * from student";// where Id='916a84c6-85cb-4b41-b52a-96a0685d91b5'";var sex = SqlMapper.Query<Dictionary<string, object>>(conn, sql, null).ToList();?
很簡單的將結(jié)果轉(zhuǎn)成?Dictionary ,它的委托實現(xiàn)如下:
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;
? ? ? ? }
如果是上面的類型,我就會用下面的委托來轉(zhuǎn)換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;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? };
? ? ? ? }
后續(xù),上面只是講到單個實體查詢的情況,至于多個實體的查詢,只需理解兩個參數(shù)
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? 如果理解了這兩個參數(shù),其他跟單個實體是一樣,至于這兩個參數(shù)我先不講解,后面我會提供源碼,自己看,或者以后抽空我再講講。
?
總結(jié):
出參就講到這吧,后續(xù)我看講不講Dapper擴展的,會提供源碼下載,源碼可能和文章會有些出入,發(fā)布文章后我修改過源碼。
相關(guān)文章:?
Dapper源碼學習和源碼修改
.Net開源微型ORM框架測評
.NET Core開發(fā):項目實踐
.NET Core 使用Dapper 操作MySQL
Dapper、Entity Framework 和混合應用
原文地址:http://www.cnblogs.com/deeround/p/6633611.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的Dapper源码学习和源码修改(下篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core MVC 源码学
- 下一篇: ASP.NET Core MVC 源码学