En utilisant dapper, pourquoi une table temporaire est-elle créée lors d'une utilisation d'une connexion non disponible dans une seconde utilisation de la même connexion?

c# dapper sql temp-tables

Question

J'essaie d'effectuer une série d'étapes SQL * Server en utilisant dapper à partir de C #. Une étape crée une table temporaire et la remplit. Les étapes suivantes interrogent les données de la table temporaire. La commande create / populate semble fonctionner correctement, mais la première requête de la table temporaire échoue, indiquant:

"Nom d'objet invalide '#GetPageOfGlobalUsers'."

        using (SqlConnection connection = DBConnectionProvider.CreateConnection())
        {
            ... misc setup stuff...

            connection.Execute(@"
                create table #GetPageOfGlobalUsers(row int, EmailAddress nvarchar(max), LastName nvarchar(max), FirstName nvarchar(max), Id uniqueidentifier)
                insert into #GetPageOfGlobalUsers
                SELECT ROW_NUMBER() OVER (order by LastName, FirstName, EmailAddress) row,
                    EmailAddress, LastName, FirstName, Id 
                    FROM Users 
                    WHERE LastName like @search or FirstName like @search or EmailAddress like @search
            ", new { search = search }
            );

            int count = connection.Query<int>(@"
                SELECT count(*) from tempdb..#GetPageOfGlobalUsers
            ").Single<int>();

... more queries from the temp table follow

Au-dessus, Execute fonctionne, mais la requête échoue avec l'erreur que j'ai mentionnée ci-dessus. (Notez que j'obtiens la même erreur, que j'utilise ou non le préfixe "tempdb ..".) Si je crée plutôt une table permanente (par exemple si je supprime le hachage principal) ou si je la transforme en table globale (préfixe le nom avec deux hachages) tout fonctionne bien.

Je crois comprendre que les tables temporaires nommées avec un hachage unique sont définies en fonction de la durée de la connexion. Je ne sais donc pas ce qui se passe. Mais je suis sûr que quelqu'un peut me le dire!

(BTW, je l'apprécierais si personne ne me dit "ne le fais pas de cette façon" à moins que cela ne soit tout simplement pas possible.)

Réponse acceptée

Je ne comprends pas exactement ce qui se passe, mais je suis en mesure de contourner le problème en créant la table temporaire dans une exécution propre, par opposition à une exécution qui à la fois crée la table et la remplit, comme dans le code montré dans ma question.

C'est-à-dire les travaux suivants:

            connection.Execute(@"
                create table #PagesOfUsers(row int, 
                                           EmailAddress nvarchar(max), 
                                           LastName nvarchar(max), 
                                           FirstName nvarchar(max), 
                                           Id uniqueidentifier)"
                );

            connection.Execute(@"
                insert into #PagesOfUsers
                SELECT ROW_NUMBER() OVER (order by LastName, FirstName, EmailAddress) row,
                    EmailAddress, LastName, FirstName, Id 
                    FROM Users 
                    WHERE LastName like @search or FirstName like @search or EmailAddress like @search
            ", new { search = search }
            );

            int count = connection.Query<int>(@"
                SELECT count(*) from #PagesOfUsers
            ").Single<int>();

Ce n'est pas horrible, mais c'est gênant. Il est à noter que je préfère ne pas avoir à créer explicitement la table temporaire. En effet, j'avais initialement codé l'opération create / populate en tant que SELECT INTO, donc je n'avais pas besoin de détailler les colonnes de la table temporaire. Mais cela a également rencontré l'erreur "objet invalide" dans la requête suivante, donc j'ai essayé le CREATE TABLE explicite pour voir si cela faisait une différence et j'ai posté ma question après avoir constaté que ce n'était pas le cas.

Le comportement que je constate est que lorsque la table temporaire est créée et remplie dans la même exécution, elle ne se trouve pas vraiment dans tempdb après la fin de l'exécution, apparemment avec succès. Cela me laisse à me demander si l'Execute dans mon code d'origine faisait quoi que ce soit! Pour tout ce que je peux dire, il s’agissait d’un NOOP.


Réponse d'expert

Ce qui suit fonctionne parfaitement pour moi:

db.Open();
db.Execute(
    @"create table #foo (val int not null);
      insert #foo (val) values (123)");
db.Execute(
    @"insert #foo (val) values (456)");
var vals = db.Query<int>(
    @"select * from #foo").ToList();
foreach(var val in vals)
    Console.WriteLine(val);

Cela fonctionne aussi parfaitement si j'utilise:

@"select * from tempdb..#foo"

La seule façon de le faire cesser de fonctionner est, comme dans ma réponse précédente, de ne pas ouvrir la connexion en premier .


Réponse d'expert

Je soupçonne que la connexion n'est pas ouverte. Si c'est le cas, dapper ouvrira et fermera (de nouveau au pool) la connexion si nécessaire. Cela réinitialisera la connexion, perdant toutes les tables temporaires entre les commandes.

Ouvrez simplement la connexion.



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