2015-11-11 2 views
1

Я только начал использовать Dapper, и у меня возникла следующая проблема.Вставить несколько значений и вернуть несколько значений

Я хочу вставить кучу записей и вернуть вставленные записи вместе с автоматически увеличивающимся идентификатором.

Использование Postgres, я хочу, чтобы запустить эквивалент этого запроса:

INSERT INTO players (name) 
VALUES ('Player1'), ('Player2'), ('Player3'), ('Player4'), ('Player5') 
RETURNING id, name; 

Использование щеголеватый для выполнения этого запроса в списке игроков и сериализация обратно в список игроков (с идентификаторами) Я думал Я мог бы сделать это:

public class Player 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

var players = new List<Player> { new Player { Name = "Player1" }, new Player { Name = "Player2" }, new Player { Name = "Player3" }, new Player { Name = "Player4" }, new Player { Name = "Player5" }} 

connection.Query<Player>("INSERT INTO players (name) VALUES (@Name) \r\n" + 
    "RETURNING id, name, tag;", 
    players); 

Это выдает следующее сообщение об ошибке (это список игроков, каждый с именем):

Parameter '@Name' referenced in SQL but not found in parameter list 

Я считаю, что Query() может не поддерживать списки параметров, поэтому я попробовал connection.Execute(). Выполнять работу, но, очевидно, он не возвращает обратно вставленных игроков с идентификаторами.

Стоит отметить, что я могу сделать INSERT и RETURNING таким образом, когда я вставляю только одно значение.

Кто-нибудь знает, как я могу сделать INSERT и RETURNING для нескольких значений, подобных этому с Dapper?

Update

У меня есть это (немного грязный) решение:

 var sb = new StringBuilder(); 
     sb.Append("INSERT INTO players (name) VALUES \r\n"); 
     var parameters = new ExpandoObject() as IDictionary<string, object>; 

     var values = new List<string>(); 
     for (int i = 0; i < players.Count; i++) 
     { 
      var p = players[i]; 

      values.Add($"(@Name{i})"); 
      parameters[$"Name{i}"] = p.Name; 
     } 

     sb.Append(string.Join(", \r\n", values)); 

     sb.Append(" \r\nRETURNING id, name, tag;"); 

     // parameters = { Name1 = "Player1", Name2 = "Player2, ... etc} 

     var ret = connection.Query<Player>(sb.ToString(), parameters); 

Так построения ExpandoObject со свойствами из моих игроков, а затем проходящее, что в Dapper Query(). Это работает, но кажется довольно грязным. Любые предложения о том, как улучшить это?

+0

Я не знаком с postgresql, поэтому не могу комментировать этот фактический запрос, но для возврата нескольких наборов результатов вам нужно использовать 'connection.QueryMultiple()'. Вот [аналогичный вопрос] (http://stackoverflow.com/questions/19337468/multiple-sql-statements-in-one-roundtrip-using-dapper-net) – markpsmith

+0

@markpsmith Спасибо за ваше предложение. QueryMultiple для нескольких SELECT, как в связанном вопросе. Я не думаю, что это применимо здесь. – janderson

ответ

1

Во-первых, следует отметить, что прохождение List<Player>к Execute метод в качестве параметра внешний по существу такой же, как:

foreach(var player in players) 
    connection.Execute(
     "INSERT INTO players (name) VALUES (@Name) \r\n" + 
     "RETURNING id, name, tag;", player); 

Щеголеватый просто разворачивает его для вас (если он не является очень специфический сценарий async, где он может конвейерно выполнять команды). Dapper делает расширение списка-параметров, но это значение для уровня листа, и было построено для использования in (...), поэтому синтаксис не получится так, как вы хотите; В качестве примера:

DateTime dateStart = ... 
int[] custIds = ... 
var orders = conn.Query<Order>(@" 
    select * from Order 
    where OrderDate >= @dateStart and CustomerId in @custIds", 
    new { dateStart, custIds }).AsList(); 

который становится SQL:

select * from Order 
where OrderDate >= @dateStart and CustomerId in (@custIds0, @custIds1, ...) 

(в зависимости от количества элементов в массиве)

Ваш ожидается использование является тот, который был предложен и обсуждался совсем недавно; в настоящее время не поддерживается - цикл работает только для Execute, однако все более вероятно, что мы добавим что-то.Сложный бит заключается в том, чтобы определить, что такое правильное поведение, и ожидает ли , что это по существу приведет к объединению результатов нескольких отдельных операций.

Однако; делать то, что вы хотите с помощью LINQ:

var results = players.SelectMany(
    player => connection.Query<Player>("...", player)).AsList(); 

Это то же самое «раскатать петлю и сцепить результаты» поведение, за исключением того, что должно работать.

+0

Спасибо за ваш ответ. Разве нет лишних накладных расходов при вызове нескольких запросов? Можно ли это зафиксировать, поместив его в транзакцию? – janderson

+0

Или есть способ, по которому вы бы предложили мне параметризовать запрос вставки непосредственно и передать его в dapper Query()? – janderson

+0

@janderson накладные расходы: конечно, есть стоимость туда-обратно (латентность); внутренности делают много для повторного использования таких вещей, как командный экземпляр и т. д. Ввод его в транзакцию ничего не делает, кроме добавления нескольких дополнительных уровней накладных расходов. Основная проблема здесь заключается в том, что вы не можете слепо конкатенировать SQL - если она объявляет переменные или создает временные таблицы/переменные таблицы: она будет ломаться. \ –