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);
转换方法
序列化方法的实例是个结构体
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);
}
}