Dapper SetTypeMap导致DapperExtensions ClassMapper失效问题咨询
SqlMapper.SetTypeMap() Yep, you’re exactly right—calling Dapper.SqlMapper.SetTypeMap(typeof(User), new UserMapper()) does override the default type mapping configuration that Dapper Extensions depends on. Here’s why this happens and how to fix it:
Why This Occurs
Dapper Extensions builds directly on top of Dapper’s core mapping system. When you set a custom ITypeMap globally for a type, you’re replacing Dapper’s default type resolver (which Dapper Extensions relies on to load its own ClassMapper configurations). Dapper will prioritize your custom ITypeMap over any mappings registered by Dapper Extensions, so when you call cn.Get<User>(id), Dapper Extensions can’t access its own column-to-property mappings anymore—hence you get default values for all properties.
Solutions to Make Them Coexist
1. Extend the Default Mapping (Recommended)
Modify your UserMapper to wrap both Dapper’s default type map and Dapper Extensions’ mappings, instead of replacing them entirely. This way, your custom logic runs alongside the existing Dapper Extensions configuration.
Here’s an example implementation:
public class UserMapper : SqlMapper.ITypeMap { private readonly ITypeMap _underlyingTypeMap; public UserMapper() { // Get the existing type map that Dapper Extensions set up _underlyingTypeMap = SqlMapper.GetTypeMap(typeof(User)) ?? new DefaultTypeMap(typeof(User)); } // Delegate to the underlying map first, then apply custom logic public ConstructorInfo FindConstructor(string[] names, Type[] types) { return _underlyingTypeMap.FindConstructor(names, types); } public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName) { return _underlyingTypeMap.GetConstructorParameter(constructor, columnName); } public SqlMapper.IMemberMap GetMember(string columnName) { // Check the existing mapping first var memberMap = _underlyingTypeMap.GetMember(columnName); if (memberMap != null) return memberMap; // Add your custom column-to-property mappings here return columnName.ToLower() switch { "user_id" => new CustomMemberMap(typeof(User).GetProperty(nameof(User.Id))), "user_fullname" => new CustomMemberMap(typeof(User).GetProperty(nameof(User.FullName))), _ => null }; } public ConstructorInfo[] GetConstructors() { return _underlyingTypeMap.GetConstructors(); } } // Helper class to implement IMemberMap for custom mappings public class CustomMemberMap : SqlMapper.IMemberMap { public Type MemberType => _property.PropertyType; public string ColumnName { get; } public MemberInfo Member => _property; public ParameterInfo Parameter { get; } private readonly PropertyInfo _property; public CustomMemberMap(PropertyInfo property) { _property = property; } }
2. Avoid Global Type Mapping (Use Per-Query Mappings)
If your custom mapping only applies to specific queries, skip the global SetTypeMap and instead specify the type map directly when executing Dapper queries. This leaves Dapper Extensions’ mappings intact for its own operations:
// For Dapper queries that need your custom mapping var users = cn.Query<User>(sql, typeMap: new UserMapper()); // Dapper Extensions calls like cn.Get<User>(id) will still use their own mappings
Key Takeaway
Dapper’s global type map is a single point of configuration—replacing it overrides all other mapping sources, including Dapper Extensions. By wrapping the existing type map in your custom ITypeMap implementation, you preserve Dapper Extensions’ functionality while adding your own custom logic.
内容的提问来源于stack exchange,提问作者Daniele Arrighi

