Передача параметров запроса в Dapper с использованием OleDb

Этот запрос вызывает ошибку 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".

Как построить строку запроса и как передать параметр?

Ответ 1

Текущий исходный код (еще не выпущенный для NuGet) решает эту проблему; должно работать следующее:

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

Ответ 2

Важно: см. более новый ответ


В текущей сборке ответ на это будет "нет" по двум причинам:

  • код пытается отфильтровать неиспользуемые параметры - и в настоящее время удаляет все из них, потому что он не может найти что-либо вроде @id, :id или ?id в sql
  • код для добавления значений из типов использует произвольный (ну, ok: алфавитный) порядок для параметров (поскольку отражение не дает никаких гарантий относительно порядка членов), делая позиционные анонимные аргументы неустойчивыми

Хорошей новостью является то, что оба они являются фиксируемыми

  • мы можем сделать условие фильтрации условным
  • мы можем определить категорию типов, у которой есть конструктор, который соответствует всем именам свойств, и использовать позиции аргумента конструктора для определения синтетического порядка свойств - анонимные типы попадают в эту категорию

Выполняя эти изменения в моем локальном клоне, теперь выполняется следующее:

// 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 разрешает его в одном месте.

Я открыт для обратной связи, прежде чем я выдвигаю это - подходит ли это для вас?


Edit: с добавлением funky 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);
    }
}

Ответ 3

Я тестировал использование Dapper в своем программном продукте, использующем odbc-соединения (на данный момент). Однако однажды я намерен отойти от odbc и использовать другой шаблон для поддержки различных продуктов RDBMS. Однако моя проблема с реализацией решения в 2 раза:

  • Я хочу написать код SQL с параметрами, которые соответствуют различным контентам, и поэтому я хочу писать именованные параметры в моем SQL сейчас, чтобы у меня не было возврата и повторного использования позже.
  • Я не хочу полагаться на получение порядка моих свойств в соответствии с моим?. Это плохо. Поэтому мое предложение - добавить поддержку Именованных параметров для odbc.

В то же время я взломал решение, которое позволяет мне делать это с помощью Dapper. По сути, у меня есть подпрограмма, которая заменяет именованные параметры? а также восстанавливает объект параметра, убедившись, что параметры находятся в правильном порядке. Однако, глядя на код Dapper, я вижу, что я повторил некоторые из того, что делает dapper в любом случае, эффективно каждое значение параметра теперь посещается еще раз, чем это необходимо. Это становится проблемой для массовых обновлений/вставок. Но, по крайней мере, это работает для меня o.k...

Я заимствовал немного кода из здесь, чтобы сформировать часть моего решения...

Ответ 4

? для параметров был частью решения для меня, но он работает только с целыми числами, как ID. Это все еще терпит неудачу для строк, потому что длина параметра не определена.

OdbcException: ОШИБКА [HY104] [Microsoft] [ODBC Microsoft Access Driver] Неверное значение точности
System.Data.Odbc. OdbcParameter.Bind (OdbcStatementHandle hstmt, команда OdbcCommand, короткий порядковый номер, параметр CNativeBufferBuffer, bool allowReentrance)
System.Data.
System.Data.Odbc.OdbcCommand.ExecuteReaderObject (поведение CommandBehavior, строковый метод, bool needReader)
System.Data.Common.DbCommand.ExecuteDbDataReaderAsync (поведение CommandBehavior, CancellationToken cancellationToken)
Dapper.SqlMapper.QueryAsync(IDbConnection cnn, введитеffectiveType, команда CommandDefinition) в SqlMapper.Async.cs
WebAPI.DataAccess.CustomerRepository.GetByState (состояние строки) в Repository.cs
var result = await conn.QueryAsync(sQuery, new {State = state});
WebAPI.Controllers.CustomerController.GetByState (состояние строки) в CustomerController.cs
return await _customerRepo.GetByState(state);

Чтобы Dapper передавал строковые параметры в ODBC, мне нужно было указать длину.

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