前言
在上次的文章中,我們讓 EF Core 6 支持了 DateOnly 類型。
那么,Dapper 是否支持 DateOnly 類型呢?
public?class?User
{public?int?Id?{?get;?set;?}public?string?Name?{?get;?set;?}public?DateOnly?Birthday?{?get;?set;?}
}using?(var?connection?=?new?SqlConnection(connectionString))
{var?users?=?connection.Query<User>("select?*?from?users");
}
也不行:
由于異常提示沒有任何指導意義,于是我們想從源碼入手解決。
深入探究
調用堆棧
通過調用堆棧,找到發生異常的位置位于SqlMapper.cs
第 1113 行:
第 1113 行具體代碼如下:
其中func
的來源是tuple.Func
,而 tuple 是通過如下方式賦值的:
tuple?=?info.Deserializer?=?new?DeserializerState(hash,?GetDeserializer(effectiveType,?reader,?0,?-1,?false));
看到Deserializer
這個單詞,立刻引起了我們的注意:序列化
GetDeserializer 方法
趕緊來看看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#?designif?(type?==?typeof(object)?||?type?==?typeof(DapperRow)){return?GetDapperRowDeserializer(reader,?startBound,?length,?returnNullIfFirstMissing);}Type?underlyingType?=?null;if?(!(typeMap.ContainsKey(type)?||?type.IsEnum?||?type.IsArray?||?type.FullName?==?LinqBinary||?(type.IsValueType?&&?(underlyingType?=?Nullable.GetUnderlyingType(type))?!=?null?&&?underlyingType.IsEnum))){if?(typeHandlers.TryGetValue(type,?out?ITypeHandler?handler)){return?GetHandlerDeserializer(handler,?type,?startBound);}return?GetTypeDeserializer(type,?reader,?startBound,?length,?returnNullIfFirstMissing);}return?GetStructDeserializer(type,?underlyingType????type,?startBound);
}
方法會從 typeHandlers 中獲取ITypeHandler
接口的實現。
而ITypeHandler
接口定義如下:
///?<summary>
///?Implement?this?interface?to?perform?custom?type-based?parameter?handling?and?value?parsing
///?</summary>
public?interface?ITypeHandler
{///?<summary>///?Assign?the?value?of?a?parameter?before?a?command?executes///?</summary>///?<param?name="parameter">The?parameter?to?configure</param>///?<param?name="value">Parameter?value</param>void?SetValue(IDbDataParameter?parameter,?object?value);///?<summary>///?Parse?a?database?value?back?to?a?typed?value///?</summary>///?<param?name="value">The?value?from?the?database</param>///?<param?name="destinationType">The?type?to?parse?to</param>///?<returns>The?typed?value</returns>object?Parse(Type?destinationType,?object?value);
}
實現此接口以執行自定義的基于類型的參數處理和值解析
這不正是我們想要的嗎?
AddTypeHandler 方法
而怎么向 Dapper 傳入ITypeHandler
實現呢?
我們最終找到了AddTypeHandler
方法:
///?<summary>
///?Configure?the?specified?type?to?be?processed?by?a?custom?handler.
///?</summary>
///?<param?name="type">The?type?to?handle.</param>
///?<param?name="handler">The?handler?to?process?the?<paramref?name="type"/>.</param>
public?static?void?AddTypeHandler(Type?type,?ITypeHandler?handler)?=>?AddTypeHandlerImpl(type,?handler,?true);
配置要由自定義處理程序處理的指定類型
實現
首先,創建ITypeHandler
實現:
public?class?DateOnlyTypeHandler?:?TypeHandler<DateOnly>
{public?override?DateOnly?Parse(object?value){return?DateOnly.FromDateTime((DateTime)value);}public?override?void?SetValue(IDbDataParameter?parameter,?DateOnly?value){parameter.Value?=?value.ToDateTime(TimeOnly.MinValue);}
}
然后,在啟動時添加自定義處理程序:
SqlMapper.AddTypeHandler<DateOnly>(new?DateOnlyTypeHandler());
現在,程序就可以正常運行了。
結論
今天,我們介紹了使用自定義ITypeHandler
來告訴 Dapper 如何處理默認不支持的數據類型。
添加微信號【MyIO666】,邀你加入技術交流群