ASP.NET Core 6 Web API项目中Dapper不支持TimeOnly类型的解决办法
解决Dapper调用ExecuteScalar时System.TimeOnly参数报错问题
问题场景
在以SQL Server 2016为后端的ASP.NET Core 6 Web API项目中使用Dapper调用ExecuteScalar方法时,出现以下错误:
The member DepartureTime of type System.TimeOnly cannot be used as a parameter value
相关代码及实体类如下:
数据插入代码
public virtual int Create(TModel entity) { int result = -1; var insertQuery = QueryGenerator.GenerateInsertQuery(typeof(TModel)); var factory = new SqlServerDbConnectionFactory(connectionString); var profiler = CustomDbProfiler.Current; using (var connection = ProfiledDbConnectionFactory.New(factory, profiler)) { try { if (connection.State == ConnectionState.Closed) { connection.ConnectionString = connectionString; connection.Open(); } result = connection.ExecuteScalar<int>(insertQuery, entity, transaction: null); // 报错位置 } catch (Exception e) { var sql = profiler.GetCommands(); throw; } } return result; }
生成的SQL语句
INSERT INTO [Flight] ([FlightNumber], [SourceCode], [DestinCode],[DepartureTime], [EffectDate], [Stops], [TravelMinutes]) VALUES (@FlightNumber, @SourceCode, @DestinCode, @DepartureTime, @EffectDate, @Stops, @TravelMinutes); SELECT SCOPE_IDENTITY()
FlightModel实体类
public class FlightModel { public int Id { get; set; } public string FlightNumber{ get; set; } public string SourceCode{ get; set; } public string DestinCode { get; set; } public TimeOnly DepartureTime{ get; set; } public DateOnly EffectDate { get; set; } public int Stops{ get; set; } public int TravelMinutes { get; set; } }
解决办法
方案1:自定义Dapper类型处理器(推荐)
由于SQL Server 2016原生不支持.NET 6+的TimeOnly/DateOnly类型,Dapper无法自动完成映射,需要自定义类型处理器实现转换逻辑:
- 创建
TimeOnly和DateOnly的类型处理器:
// TimeOnly类型处理器 public class TimeOnlyTypeHandler : SqlMapper.TypeHandler<TimeOnly> { public override void SetValue(IDbDataParameter parameter, TimeOnly value) { // 将TimeOnly转换为SQL Server支持的TimeSpan类型 parameter.Value = value.ToTimeSpan(); } public override TimeOnly Parse(object value) { // 将从数据库读取的TimeSpan转换回TimeOnly return TimeOnly.FromTimeSpan((TimeSpan)value); } } // DateOnly类型处理器 public class DateOnlyTypeHandler : SqlMapper.TypeHandler<DateOnly> { public override void SetValue(IDbDataParameter parameter, DateOnly value) { // 将DateOnly转换为SQL Server支持的DateTime类型 parameter.Value = value.ToDateTime(TimeOnly.MinValue); } public override DateOnly Parse(object value) { // 将从数据库读取的DateTime转换回DateOnly return DateOnly.FromDateTime((DateTime)value); } }
- 在项目启动时注册这些处理器(比如
Program.cs中):
SqlMapper.AddTypeHandler(new TimeOnlyTypeHandler()); SqlMapper.AddTypeHandler(new DateOnlyTypeHandler());
注册后,Dapper会自动处理TimeOnly和DateOnly类型的参数映射,无需修改原有业务代码。
方案2:手动转换参数类型
如果不想全局注册类型处理器,可以在插入时手动将TimeOnly/DateOnly转换为SQL Server支持的类型,使用DynamicParameters传递参数:
public virtual int Create(TModel entity) { int result = -1; var insertQuery = QueryGenerator.GenerateInsertQuery(typeof(TModel)); var factory = new SqlServerDbConnectionFactory(connectionString); var profiler = CustomDbProfiler.Current; using (var connection = ProfiledDbConnectionFactory.New(factory, profiler)) { try { if (connection.State == ConnectionState.Closed) { connection.ConnectionString = connectionString; connection.Open(); } // 手动构建参数并转换类型 var parameters = new DynamicParameters(); parameters.Add("@FlightNumber", entity.FlightNumber); parameters.Add("@SourceCode", entity.SourceCode); parameters.Add("@DestinCode", entity.DestinCode); parameters.Add("@DepartureTime", ((FlightModel)(object)entity).DepartureTime.ToTimeSpan()); parameters.Add("@EffectDate", ((FlightModel)(object)entity).EffectDate.ToDateTime(TimeOnly.MinValue)); parameters.Add("@Stops", entity.Stops); parameters.Add("@TravelMinutes", entity.TravelMinutes); result = connection.ExecuteScalar<int>(insertQuery, parameters, transaction: null); } catch (Exception e) { var sql = profiler.GetCommands(); throw; } } return result; }
方案3:升级Dapper版本(辅助)
确保使用的是较新版本的Dapper(2.0+),新版本对.NET 6的新类型有更好的兼容性,但由于SQL Server 2016本身不支持TimeOnly/DateOnly,仍需配合上述类型转换方案使用。
内容的提问来源于stack exchange,提问作者Sujoy
相关产品推荐
相关产品推荐

