Dapper Object must implement IConvertible.

Dapper查询抛错误 Object must implement IConvertible.

原因

Dapper数据库和实体的映射关系进行缓存,查询字段作为缓存的Key,如果相同的查询字段对应多个实体就会报错。具体看下面代码

解决方案

SQL语句中不要出现相同的字段对应多个类,查询中字段(名称、数量)和实体保持一对一关系
错误示例:

select a,b from t1 -> class t1 {  string a { get;set; } string b { get;set; }  }
select a,b from t2 -> class t2 {  string a { get;set; } string b { get;set; }  }

错误信息

System.InvalidCastException : Object must implement IConvertible.
错误信息

 消息: 
    System.InvalidCastException : Object must implement IConvertible.
  堆栈跟踪: 
    Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
    SqlMapper.QueryImpl[T](IDbConnection cnn, CommandDefinition command, Type effectiveType)+MoveNext() 行 1105
    List`1.ctor(IEnumerable`1 collection)
    Enumerable.ToList[TSource](IEnumerable`1 source)
    SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) 行 721
    SqlMapperExtensions.GetPageAll[T](IDbConnection connection, Int32 page, Int32 perPageSize, Expression`1 sortPredicate, ISpecification`1 specification, Boolean isAsc, IDbTransaction transaction, Nullable`1 commandTimeout) 行 340
    DapperRepositoryContext.GetPage[TEntity,TKey](Int32 page, Int32 perPageSize, Expression`1 sortPredicate, Boolean isAsc, ISpecification`1 specification) 行 259
    RepositoryBase`2.GetPage(Int32 page, Int32 perPageSize, Expression`1 sortPredicate, Boolean isAsc, ISpecification`1 specification) 行 287
    RepositoryBaseTests.GetPageTest() 行 617

抛错代码示例

public class TableAction
{
     public string Guid { get; set; }

     public string Code { get; set; }    

     public string Name { get; set; }   

 }
 public class TableObject
 {
     public string Guid { get; set; }

     public string Code { get; set; }

     public string Name { get; set; } 

     public string Description { get; set; }
 }

using (var conn = new Npgsql.NpgsqlConnection("User ID=postgres;Password=Bio!@#456;Host=192.168.1.182;Port=5432;Database=bup_dips;Pooling=true;"))
{
	conn.Open();
	var sql_action = "select guid , code ,name from bup_t_action limit 1;";
	var sql_object = "select guid , code ,name from bup_t_action_object  limit 1;";
	//下面这句能执行成功
	var tableAction = await conn.QueryFirstAsync<TableAction>(sql_action);
	
	//这句执行失败
	var tableObject = await conn.QueryFirstAsync<TableObject>(sql_object);
	/*  消息: 
	    System.InvalidCastException : Object must implement IConvertible.
	  堆栈跟踪: 
	    Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
	    SqlMapper.QueryRowAsync[T](IDbConnection cnn, Row row, Type effectiveType, CommandDefinition command) 行 508
	    RepositoryBaseTests.GetPageTest() 行 632
	    --- End of stack trace from previous location where exception was thrown ---
	    */
}	

原因分析-Dapper源码解读

执行查询

执行查询前会缓存中获取转换方法,如果没有在创建,后面会看到缓存Key为查询字段,暂时只知道读取缓存就好

var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());

var info = GetCacheInfo(identity, param, command.AddToCache);

Dapper查询执行方法

转换方法

序列化方法的实例是个结构体

 private struct DeserializerState
 {
      public readonly int Hash;
      public readonly Func<IDataReader, object> Func;

      public DeserializerState(int hash, Func<IDataReader, object> func)
      {
          Hash = hash;
          Func = func;
      }
  }

获取序列化对象
获取转换方法
获取转化方法

Key

转换实体

Value

获取映射对象

总结

while (reader.Read())
{
    object val = func(reader);
    //reader相同返回列,object val始终为最先执行的class:假设为A(class)
    //如果T为A ,则 val == null || val is T 为 true,能够强转成功
    if (val == null || val is T)
    {
        yield return (T)val;
    }
    //后执行的查询,假设对象T不是A,就会在这里报错
    //Convert.ChangeType转换失败
    //将A强转为T(类B、类C、类D)抛出错误
    //System.InvalidCastException : Object must implement IConvertible.
    else
    {
        yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
    }
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值