使用Dapper为自定义Double包装结构体创建类型处理器遇注册失效问题
解决Dapper中自定义结构体MyDouble在TVP保存时的类型不支持问题
看起来你遇到的核心问题是Dapper的自定义TypeHandler默认不会自动应用到Table-Valued Parameter(TVP,即你用AddTable传入的表参数)的字段映射上,再加上你在Save方法里调用了SqlMapper.ResetTypeHandlers(),反而清除了刚注册的处理器,导致类型映射完全失效。下面给你一步步的解决方案:
1. 先修正TypeHandler的注册时机
首先删掉Save方法里的SqlMapper.ResetTypeHandlers(),这行代码会清除所有已注册的类型处理器(包括你刚加的MyDoubleTypeHandler),完全没必要每次调用Save都重置。
正确的做法是在应用启动时一次性注册TypeHandler,比如在ASP.NET Core 2.1的Startup.cs的ConfigureServices方法里:
public void ConfigureServices(IServiceCollection services) { // 注册自定义类型处理器,只需要执行一次 SqlMapper.AddTypeHandler(new MyDoubleTypeHandler()); // 同时告诉Dapper,MyDouble对应的数据库类型是Double SqlMapper.AddTypeMap(typeof(MyDouble), DbType.Double); // 其他服务注册... }
2. 适配TVP的自定义类型处理
Dapper.ParameterExtensions的AddTable方法在构建DataTable时,并不会主动使用你注册的TypeHandler来转换自定义类型。所以我们需要手动构建符合要求的DataTable,而不是依赖AddTable:
修改你的Save方法如下:
public async Task<bool> Save(List<MyObject> data) { var parameters = new DynamicParameters(); // 1. 手动构建对应数据库TVP的DataTable var dataTable = new DataTable(); // 这里的列名和类型要和数据库里的TVP(dbo.MyObject)完全匹配 dataTable.Columns.Add("Prop1", typeof(double)); // 2. 将MyDouble转换为double后加入DataTable foreach (var item in data) { // 利用你定义的显式转换,把MyDouble转成double dataTable.Rows.Add((double)item.Prop1); } // 3. 添加表值参数,注意指定TypeName为数据库中TVP的全名 parameters.Add("@Data", dataTable, DbType.Object, ParameterDirection.Input); // 获取底层SqlParameter,设置TypeName(必须和数据库的TVP名称一致) var sqlParam = parameters.Get<SqlParameter>("@Data"); sqlParam.TypeName = "dbo.MyObject"; using (var connection = new SqlConnection(connectionString)) { await connection.OpenAsync(); // ExecuteAsync返回受影响的行数,根据业务需求判断返回值 int affectedRows = await connection.ExecuteAsync("name-of-sp", parameters, commandType: CommandType.StoredProcedure); return affectedRows > 0; } }
3. 额外检查点
- 确认数据库中的TVP(
dbo.MyObject)的Prop1列类型是float(对应.NET的double/DbType.Double),类型不匹配也会导致报错。 - 验证你的
MyDouble结构体的显式转换是否正常:(double)myDoubleInstance能正确返回内部的value值。
内容的提问来源于stack exchange,提问作者user1820686
相关产品推荐
相关产品推荐

