Insérer plusieurs valeurs et renvoyer plusieurs valeurs

c# dapper postgresql sql

Question

Je viens de commencer à utiliser Dapper et j'ai rencontré le problème suivant.

Je veux insérer un tas d'enregistrements et retourner les enregistrements insérés à côté de l'identifiant auto-incrémenté.

En utilisant Postgres, je veux exécuter l'équivalent de cette requête:

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

En utilisant Dapper pour exécuter cette requête sur une liste de joueurs et en la sérialisant dans une liste de joueurs (avec les identifiants), je pensais pouvoir faire ceci:

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);

Cela génère l'erreur suivante (c'est une liste de joueurs ayant chacun un nom):

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

Je crois que Query () peut ne pas prendre en charge les listes de paramètres, alors j'ai essayé connection.Execute () à la place. Exécuter fonctionne, mais évidemment, il ne renvoie pas les lecteurs insérés avec leurs identifiants.

Il est à noter que je peux faire un INSERT et un RETOUR comme ceci lorsque je n’insère qu’une seule valeur.

Est-ce que quelqu'un sait comment faire INSERT et RETURNING pour plusieurs valeurs comme celle-ci avec Dapper?

Mettre à jour

J'ai cette solution (un peu sale):

        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);

Donc, construisez un ExpandoObject avec les propriétés de mes joueurs, puis transmettez-le dans Dapper Query (). Ça marche, mais ça semble plutôt sale. Des suggestions sur la façon d'améliorer cela?

Réponse d'expert

Tout d'abord, il convient de noter que le fait de transmettre une List<Player> à la méthode Execute tant que paramètre le plus externe est essentiellement identique à:

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

Dapper le déroule juste pour vous (sauf s'il s'agit d'un scénario async très spécifique où il peut canaliser les commandes). Dapper prend en charge l'extension des paramètres de liste, mais c'est pour les valeurs de niveau feuille, et a été construit pour in (...) utilisation, de sorte que la syntaxe ne serait pas comme vous le souhaitez; par exemple:

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

qui devient le SQL:

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

(en fonction du nombre d'éléments dans le tableau)

Votre utilisation attendue est celle qui a été suggérée et discutée assez récemment. à l'heure actuelle, ce n'est pas supporté - le déroulement de la boucle ne fonctionne que pour Execute , cependant, il est de plus en plus probable que nous ajouterons quelque chose ici. Le problème est de savoir quel est le comportement correct et s’il est prévu que cela concaténera essentiellement les résultats de plusieurs opérations distinctes.

Toutefois; faire ce que vous voulez via LINQ:

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

C'est le même comportement "dérouler la boucle et concaténer les résultats", sauf que cela devrait fonctionner.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi