Dapper C#报错:需无参默认构造函数或匹配签名构造函数的解决方法
问题:Dapper查询MySQL分区列表时报物化类型构造函数不匹配错误
实现获取指定数据表分区列表的初始代码如下:
public record Partition(string PartitionName, int OrdinalPosition); public class MysqlService { //... public IEnumerable<Partition> GetPartitions(string tableName) { using var conn = new MySqlConnection(_dbConnString); conn.Open(); return conn.Query<Partition>( $"SELECT PARTITION_NAME, PARTITION_ORDINAL_POSITION " + $"FROM information_schema.partitions WHERE TABLE_NAME = '{tableName}';"); } }
运行上述代码抛出如下异常:
Unhandled exception. System.InvalidOperationException: A parameterless default constructor or one matching signature (System.String PARTITION_NAME, System.UInt64 PARTITION_ORDINAL_POSITION) is required for Services.Partition materialization at Dapper.SqlMapper.GenerateDeserializerFromMap(Type type, IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing, ILGenerator il) in /_/Dapper/SqlMapper.cs:line 3297 at Dapper.SqlMapper.GetTypeDeserializerImpl(Type type, IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in /_/Dapper/SqlMapper.cs:line 3131 at Dapper.SqlMapper.TypeDeserializerCache.GetReader(IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in /_/Dapper/SqlMapper.TypeDeserializerCache.cs:line 151 at Dapper.SqlMapper.TypeDeserializerCache.GetReader(Type type, IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in /_/Dapper/SqlMapper.TypeDeserializerCache.cs:line 50 at Dapper.SqlMapper.GetTypeDeserializer(Type type, IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in /_/Dapper/SqlMapper.cs:line 3085 at Dapper.SqlMapper.GetDeserializer(Type type, IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in /_/Dapper/SqlMapper.cs:line 1835 at Dapper.SqlMapper.QueryImpl[T](IDbConnection cnn, CommandDefinition command, Type effectiveType)+MoveNext() in /_/Dapper/SqlMapper.cs:line 1105 at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in /_/Dapper/SqlMapper.cs:line 734 ...
已尝试将OrdinalPosition属性的类型从int替换为UInt64,仍然触发相同报错。
运行环境信息:
- C# .NET Core 控制台应用程序(net6.0)
- Dapper 2.0.123
- MySQL 8.0
报错原因
异常信息已经明确给出了Dapper查找的构造函数签名要求:(System.String PARTITION_NAME, System.UInt64 PARTITION_ORDINAL_POSITION),之前的修改只解决了类型不匹配问题,没有解决构造函数参数名和查询返回列名不匹配的核心问题:
- MySQL
information_schema.partitions表的PARTITION_ORDINAL_POSITION字段是无符号bigint类型,对应C#UInt64(别名ulong),初始代码用int确实存在类型不匹配。 - Dapper对record类型的主构造函数匹配规则是按参数名和查询列名精确匹配(大小写敏感),定义的record主构造函数参数名是帕斯卡命名的
PartitionName、OrdinalPosition,和SQL返回的全大写下划线命名的列名无法对应,就算类型改对了也找不到匹配的构造函数。 - 初始代码用字符串拼接方式传入表名,存在SQL注入风险。
修复方案
任选以下一种方案即可解决问题,推荐优先选方案1,代码侵入性最低:
方案1:SQL查询时给列起别名,匹配record参数名,同时修正类型和参数化传值
将OrdinalPosition类型改为ulong,在SQL中给返回列起和record构造参数完全一致的别名,同时用Dapper的参数化传值避免注入风险:
// 修正OrdinalPosition类型为无符号64位整型 public record Partition(string PartitionName, ulong OrdinalPosition); public class MysqlService { // 数据库连接字符串字段,原有逻辑保留 private readonly string _dbConnString; public IEnumerable<Partition> GetPartitions(string tableName) { // 基础参数校验 if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentException("表名不能为空", nameof(tableName)); using var conn = new MySqlConnection(_dbConnString); conn.Open(); // 列别名和record参数名完全一致,参数化传值 return conn.Query<Partition>( @"SELECT PARTITION_NAME AS PartitionName, PARTITION_ORDINAL_POSITION AS OrdinalPosition FROM information_schema.partitions WHERE TABLE_NAME = @tableName", new { tableName } ); } }
方案2:给Partition类型添加无参构造函数
给record定义无参构造函数,将属性设置为可初始化的公开属性,Dapper会通过无参构造创建实例后自动匹配属性赋值,此时属性名和列名的匹配默认不区分大小写,也支持下划线转驼峰的全局配置:
public record Partition { // 必须提供公开无参构造函数 public Partition() {} public string PartitionName { get; init; } public ulong OrdinalPosition { get; init; } }
方案3:通过特性显式配置列名映射
给record的属性添加列映射特性,显式指定对应数据库列名,不需要修改SQL:
using System.ComponentModel.DataAnnotations.Schema; // 给主构造函数参数添加Column特性指定映射列名 public record Partition( [Column("PARTITION_NAME")] string PartitionName, [Column("PARTITION_ORDINAL_POSITION")] ulong OrdinalPosition );
内容的提问来源于stack exchange,提问作者Ellisein
相关产品推荐
相关产品推荐

