You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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),之前的修改只解决了类型不匹配问题,没有解决构造函数参数名和查询返回列名不匹配的核心问题:

  1. MySQL information_schema.partitions 表的PARTITION_ORDINAL_POSITION字段是无符号bigint类型,对应C# UInt64(别名ulong),初始代码用int确实存在类型不匹配。
  2. Dapper对record类型的主构造函数匹配规则是按参数名和查询列名精确匹配(大小写敏感),定义的record主构造函数参数名是帕斯卡命名的PartitionName、OrdinalPosition,和SQL返回的全大写下划线命名的列名无法对应,就算类型改对了也找不到匹配的构造函数。
  3. 初始代码用字符串拼接方式传入表名,存在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 12:42:17