目录
前言
深入探究
调用堆栈
GetDeserializer 方法
AddTypeHandler 方法
实现
结论
前言
在上次的文章中,我们让 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("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
方法的实现:
private static Func 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))
{
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
接口定义如下:
///
/// Implement this interface to perform custom type-based parameter handling and value parsing
///
public interface ITypeHandler
{
///
/// Assign the value of a parameter before a command executes
///
/// The parameter to configure
/// Parameter value
void SetValue(IDbDataParameter parameter, object value);
///
/// Parse a database value back to a typed value
///
/// The value from the database
/// The type to parse to
/// The typed value
object Parse(Type destinationType, object value);
}
实现此接口以执行自定义的基于类型的参数处理和值解析
这不正是我们想要的吗?
AddTypeHandler 方法而怎么向 Dapper 传入ITypeHandler
实现呢?
我们最终找到了AddTypeHandler
方法:
///
/// Configure the specified type to be processed by a custom handler.
///
/// The type to handle.
/// The handler to process the .
public static void AddTypeHandler(Type type, ITypeHandler handler) => AddTypeHandlerImpl(type, handler, true);
配置要由自定义处理程序处理的指定类型
实现首先,创建ITypeHandler
实现:
public class DateOnlyTypeHandler : TypeHandler
{
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(new DateOnlyTypeHandler());
现在,程序就可以正常运行了。
结论今天,我们介绍了使用自定义ITypeHandler
来告诉 Dapper 如何处理默认不支持的数据类型。