【问题标题】:Passing query parameters in Dapper using OleDb使用 OleDb 在 Dapper 中传递查询参数
【发布时间】:2019-11-21 03:59:17
【问题描述】:

此查询产生错误No value given for one or more required parameters

using (var conn = new OleDbConnection("Provider=..."))
{
  conn.Open();
  var result = conn.Query(
    "select code, name from mytable where id = ? order by name",
    new { id = 1 });
}

如果我将查询字符串更改为:... where id = @id ...,我会得到一个错误:Must declare the scalar variable "@id".

如何构造查询字符串以及如何传递参数?

【问题讨论】:

  • 获取信息:我已经更新了 github 代码,但我想在构建之前解决未解决的问题并拉取请求。如果你从 github 克隆并在本地构建,它应该可以工作

标签: oledb dapper


【解决方案1】:

以下应该有效:

var result = conn.Query(
"select code, name from mytable where id = ?id? order by name",
new { id = 1 });

【讨论】:

  • 为什么不用@Id 用于 oledb?
  • 这个问题还会阻止 DapperContrib 'Insert' 扩展工作吗?尝试使用 OleDbConnection 使用“con.Insert(entity)”时出现“必须声明标量变量”错误。
  • @Neutrino 使用 'DapperContrib' 对此问题有何更新?
【解决方案2】:

重要:see newer answer


在当前的构建中,答案是“否”,原因有两个:

  • 代码尝试过滤未使用的参数 - 目前正在删除所有参数,因为它在 sql 中找不到像 @id:id?id 这样的东西
  • 从类型中添加值的代码对参数使用任意(好吧,按字母顺序)顺序(因为反射不能保证成员的顺序),从而使位置匿名参数不稳定

好消息是这两个都是可以修复的

  • 我们可以使过滤行为有条件
  • 我们可以检测具有与所有属性名称匹配的构造函数的类型类别,并使用构造函数参数位置来确定属性的合成顺序 - 匿名类型属于此类

对我的本地克隆进行这些更改,以下内容现在通过:

// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
    using (var conn = new System.Data.OleDb.OleDbConnection(
        Program.OleDbConnectionString))
    {
        var row = conn.Query("select Id = ?, Age = ?", new DynamicParameters(
            new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
        ) { RemoveUnused = false } ).Single();
        int age = row.Age;
        int id = row.Id;
        age.IsEqualTo(23);
        id.IsEqualTo(12);
    }
}

请注意,我目前在这里使用DynamicParameters 以避免向Query / Query<T> 添加更多重载 - 因为这需要添加到相当多的方法中。将其添加到 DynamicParameters 即可在一处解决。

在我推送这个之前,我愿意接受反馈 - 你觉得这有用吗?


编辑:加上时髦的smellsLikeOleDb(不,不是开玩笑),我们现在可以更直接地做到这一点:

// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
    using (var conn = new System.Data.OleDb.OleDbConnection(
        Program.OleDbConnectionString))
    {
        var row = conn.Query("select Id = ?, Age = ?",
            new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
        ).Single();
        int age = row.Age;
        int id = row.Id;
        age.IsEqualTo(23);
        id.IsEqualTo(12);
    }
}

【讨论】:

  • 唯一变得混乱的是当您在查询中多次使用相同的参数时。鉴于我们没有任何命名(由于 OLEDB 语法),我们现在需要将参数传递为(伪代码):..."select * from people p, users u where p.id = ? and u.personId = ?", new { a = aPerson.Id, b = aPerson.Id }...
  • 难道我们不能做得更好吗,在某种意义上使用通常的参数命名,然后根据一些判别式(决定它是否是 OLEDB 查询)用 ? 重写参数,也正确调整传递的参数。不确定我是否清楚地解释了自己。感谢您的努力顺便说一句...
  • @Juri 因为装饰器模式,识别出这真的很难
【解决方案3】:

我正在使用 odbc 连接的软件产品中试用 Dapper(目前)。但是有一天我打算离开 odbc 并使用不同的模式来支持不同的 RDBMS 产品。但是,我的解决方案实施问题是 2 倍:

  1. 我想编写带有符合不同后端的参数的 SQL 代码,所以我现在想在我的 SQL 中编写命名参数,这样我以后就不用回去重做了。李>
  2. 我不想依赖让我的属性的顺序与我的?。这是不好的。所以我的建议是请为 odbc 添加对命名参数的支持。

与此同时,我已经编写了一个解决方案,让我可以使用 Dapper 执行此操作。本质上,我有一个例程将命名参数替换为 ?并且还重建参数对象以确保参数的顺序正确。 但是查看 Dapper 代码,我可以看到我已经重复了一些 dapper 正在做的事情,实际上每个参数值现在都被访问了一次,而不是必要的。这成为批量更新/插入的更多问题。 但至少它似乎对我有用...

我从here 借了一些代码来构成我的解决方案的一部分...

【讨论】:

    【解决方案4】:

    那个? for parameters 是我解决方案的一部分,但它仅适用于整数,如 ID。字符串仍然失败,因为未指定参数长度。

    OdbcException: ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]无效的精度值
    系统.数据.Odbc。 OdbcParameter.Bind(OdbcStatementHandle hstmt, OdbcCommand 命令,短序号,CNativeBuffer parameterBuffer,bool allowReentrance)
    System.Data.Odbc.OdbcParameterCollection.Bind(OdbcCommand 命令,CMDWrapper cmdWrapper,CNativeBuffer parameterBuffer) System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior 行为,字符串方法,bool needReader,object[] methodArguments,SQL_API odbcApiMethod)
    System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior 行为,字符串方法,bool needReader)
    System.Data.Common.DbCommand.ExecuteDbDataReaderAsync(CommandBehavior 行为,CancellationToken cancelToken)
    Dapper.SqlMapper.QueryAsync(IDbConnection cnn, Type EffectiveType, CommandDefinition command) in SqlMapper.Async.cs
    WebAPI.DataAccess.CustomerRepository.GetByState(string state) 在 Repository.cs
    var result = await conn.QueryAsync(sQuery, new { State = state });
    CustomerController.cs中的WebAPI.Controllers.CustomerController.GetByState(字符串状态)
    return await _customerRepo.GetByState(state);

    为了让 Dapper 将字符串参数传递给 ODBC,我必须指定长度。

    var result = await conn.QueryAsync<Customer>(sQuery, new { State = new DbString { Value = state, IsFixedLength = true, Length = 4} });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多