在 C# 中使用 SQLite 的 Dapper 查询抛出错误“'必须为以下参数添加值”
Dapper query with SQLite in C# throws error "'Must add values for the following parameters"
我正在尝试对 SQLite 3 数据库执行以下 Dapper 查询:
conn.QuerySingleOrDefault<DalTableConfig>
("select id, modelId, tableName, schemaName
from tables where tableName = $tableName;", new {tableName});
执行这一行,我得到这个错误:
System.InvalidOperationException: 'Must add values for the following parameters: $tableName'
Google 得出了相当多的结果,一个常见的结果是参数区分大小写,但这在此处不适用,大小写匹配。
Whosebug 有一个 other question 有一个未被接受但有效的答案,解决方法如下:
var parameters = new DynamicParameters();
parameters.Add("@tableName" , tableName);
conn.QuerySingleOrDefault<DalTableConfig>("select id, modelId, tableName, schemaName from tables where tableName = $tableName;", parameters);
这没有我之前得到的异常,但我仍然想知道为什么更简单的匿名对象参数不起作用。
我调试了 Dapper 源代码,发现了问题出现的地方。 SqlMapper
class 的 QuerySingleOrDefault
方法的第二行抛出异常:
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None);
return QueryRowImpl<T>(cnn, Row.SingleOrDefault, ref command, typeof(T));
此处 param
包含正确的 tableName
属性,command
在其 Parameters
集合中包含此内容。当我检查 QueryRowImpl
方法时,我发现这段代码:
private static T QueryRowImpl<T>(IDbConnection cnn, Row row, ref CommandDefinition command, Type effectiveType)
{
object param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand cmd = null;
IDataReader reader = null;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
cmd = command.SetupCommand(cnn, info.ParamReader);
if (wasClosed) cnn.Open();
reader = ExecuteReaderWithFlagsFallback(cmd, wasClosed, (row & Row.Single) != 0
? CommandBehavior.SequentialAccess | CommandBehavior.SingleResult // need to allow multiple rows, to check fail condition
: CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow);
下面这行失败了。就是这个方法抛出了异常:
ExecuteReaderWithFlagsFallback
在上面的代码中,CommandDefinition command
参数仍然有一个有效的 Parameters
集合,但是局部变量 IDbCommand cmd
来自 SetupCommand
,奇怪的是,两个空 Parameters
集合。所以 SetupCommand
或 info.ParamReader
似乎有一些错误导致参数丢失
我能够在测试中重现这个问题。下面的测试表明,当在 SQL.
中使用 @tableName
而不是 $tableName
时,OP 的原始代码有效
public class DalTableConfig
{
public int Id { get; set; }
public string ModelId { get; set; }
public string TableName { get; set; }
public string SchemaName { get; set; }
}
public class DapperTests
{
[Fact]
public void WhosebugDapperTest()
{
using var conn = new SqliteConnection("Data Source=test.db");
conn.Execute("drop table if exists tables");
conn.Execute("create table tables(id int, modelId varchar(255), tableName varchar(255), schemaName varchar(255))");
conn.Execute("insert into tables select 1, 'modelId1', 'tableName1', 'schemaName1'");
const string sql = @"
select
id
,modelId
,tableName
,schemaName
from
tables
where
tableName = @tableName"; // here's the fix
const string tableName = "tableName1";
var actual = conn.QuerySingleOrDefault<DalTableConfig>(sql, new {tableName});
Assert.NotNull(actual);
Assert.Equal(tableName, actual.TableName);
}
}
我正在尝试对 SQLite 3 数据库执行以下 Dapper 查询:
conn.QuerySingleOrDefault<DalTableConfig>
("select id, modelId, tableName, schemaName
from tables where tableName = $tableName;", new {tableName});
执行这一行,我得到这个错误:
System.InvalidOperationException: 'Must add values for the following parameters: $tableName'
Google 得出了相当多的结果,一个常见的结果是参数区分大小写,但这在此处不适用,大小写匹配。
Whosebug 有一个 other question 有一个未被接受但有效的答案,解决方法如下:
var parameters = new DynamicParameters();
parameters.Add("@tableName" , tableName);
conn.QuerySingleOrDefault<DalTableConfig>("select id, modelId, tableName, schemaName from tables where tableName = $tableName;", parameters);
这没有我之前得到的异常,但我仍然想知道为什么更简单的匿名对象参数不起作用。
我调试了 Dapper 源代码,发现了问题出现的地方。 SqlMapper
class 的 QuerySingleOrDefault
方法的第二行抛出异常:
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None);
return QueryRowImpl<T>(cnn, Row.SingleOrDefault, ref command, typeof(T));
此处 param
包含正确的 tableName
属性,command
在其 Parameters
集合中包含此内容。当我检查 QueryRowImpl
方法时,我发现这段代码:
private static T QueryRowImpl<T>(IDbConnection cnn, Row row, ref CommandDefinition command, Type effectiveType)
{
object param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand cmd = null;
IDataReader reader = null;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
cmd = command.SetupCommand(cnn, info.ParamReader);
if (wasClosed) cnn.Open();
reader = ExecuteReaderWithFlagsFallback(cmd, wasClosed, (row & Row.Single) != 0
? CommandBehavior.SequentialAccess | CommandBehavior.SingleResult // need to allow multiple rows, to check fail condition
: CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow);
下面这行失败了。就是这个方法抛出了异常:
ExecuteReaderWithFlagsFallback
在上面的代码中,CommandDefinition command
参数仍然有一个有效的 Parameters
集合,但是局部变量 IDbCommand cmd
来自 SetupCommand
,奇怪的是,两个空 Parameters
集合。所以 SetupCommand
或 info.ParamReader
似乎有一些错误导致参数丢失
我能够在测试中重现这个问题。下面的测试表明,当在 SQL.
中使用@tableName
而不是 $tableName
时,OP 的原始代码有效
public class DalTableConfig
{
public int Id { get; set; }
public string ModelId { get; set; }
public string TableName { get; set; }
public string SchemaName { get; set; }
}
public class DapperTests
{
[Fact]
public void WhosebugDapperTest()
{
using var conn = new SqliteConnection("Data Source=test.db");
conn.Execute("drop table if exists tables");
conn.Execute("create table tables(id int, modelId varchar(255), tableName varchar(255), schemaName varchar(255))");
conn.Execute("insert into tables select 1, 'modelId1', 'tableName1', 'schemaName1'");
const string sql = @"
select
id
,modelId
,tableName
,schemaName
from
tables
where
tableName = @tableName"; // here's the fix
const string tableName = "tableName1";
var actual = conn.QuerySingleOrDefault<DalTableConfig>(sql, new {tableName});
Assert.NotNull(actual);
Assert.Equal(tableName, actual.TableName);
}
}