如何在Dapper中实现DTO自定义键类型到CLR类型的转换
解决Dapper/Dapper.Contrib对自定义键类型的自动转换问题
问题核心
你用自定义Key结构体作为DTO的主键字段,使用Dapper.Contrib的Insert<T>方法时抛出System.NotSupportedException——因为Dapper无法识别自定义的键类型。你希望不用手动转换参数,让Dapper自动处理自定义类型与数据库兼容CLR类型的转换。
解决方案
Dapper提供了**自定义类型处理器(TypeHandler)**机制,可以让你指定自定义类型如何与数据库类型互转。只需要实现SqlMapper.TypeHandler<T>接口并注册,Dapper就会自动用这个处理器处理对应类型的参数和结果。
步骤1:实现自定义类型处理器
针对你的Key结构体,创建类型处理器:
public class KeyTypeHandler : SqlMapper.TypeHandler<Key> { // 从数据库读取值转换为Key类型 public override Key Parse(object value) { return (int)value; // 利用已定义的隐式转换 } // 将Key类型的值设置到数据库参数中 public override void SetValue(IDbDataParameter parameter, Key value) { parameter.DbType = DbType.Int32; parameter.Value = (int)value; // 利用已定义的隐式转换 } }
步骤2:注册类型处理器
在程序初始化阶段(比如Main方法开头)注册处理器,让Dapper识别自定义类型:
SqlMapper.AddTypeHandler(new KeyTypeHandler());
步骤3:修改原有代码测试
现在你的代码可以直接使用Dapper.Contrib的Insert方法,无需手动转换参数:
static void Main(string[] args) { // 注册自定义类型处理器 SqlMapper.AddTypeHandler(new KeyTypeHandler()); using (var connection = new SqlConnection("server=.;database=equus;integrated security=true;Encrypt=false")) { connection.Open(); connection.Execute(@"If not exists (select name from sysobjects where name = 'User') CREATE TABLE dbo.[User](UserId int identity(1,1) primary key clustered, Name varchar(200))"); var user = new User() { Name ="OSO", UserId = 1 }; connection.Insert(user); // 现在可正常执行,无需手动转换 connection.Close(); } } public struct Key { private readonly int _value; public Key(int value) { _value = value; } public static implicit operator Key(int value) { return new Key(value); } public static implicit operator int(Key key) { return key._value; } } public class User { public Key UserId { get; set; } public string Name { get; set; }=string.Empty; }
补充说明
- 若需要支持可空的
Key?类型,可额外实现NullableKeyTypeHandler,或者调整处理器为泛型兼容可空类型。 - 该机制对Dapper.Contrib的
Update<T>、Get<T>等方法同样生效,注册一次后全局可用。 - 未来如果需要修改
Key的底层类型(比如从int改为long),只需调整Key结构体的隐式转换逻辑和处理器中的DbType,无需改动业务代码,完全符合你抽象键类型的初衷。
内容的提问来源于stack exchange,提问作者Yván Ecarri
相关产品推荐
相关产品推荐

