Message d'erreur "La connection a été désactivée" lors de la création / suppression rapide de bases de données

introduction

J'écris une application Web (C # / ASP.NET MVC 3, .NET Framework 4, MS SQL Server 2008, System.Data.ODBC pour les connections de database) et j'ai quelques problèmes concernant la création / suppression de database.

J'ai une exigence que l'application devrait être capable de créer et supprimer des bases de données.

Problème

L'application échoue aux tests de résistance pour cette fonction. Plus précisément, si le client commence rapidement à créer, supprimer, créer à nouveau une database avec le même nom, puis finalement (~ à la 5ème request) le code du server lance ODBCException 'La connection a été désactivée.'. Ce comportement est observé sur toutes les machines sur lesquelles le test a été effectué – la requête exacte défaillante peut ne pas être 5ème mais quelque part autour de cette valeur.

Recherche

Googling sur exception a donné un très faible rendement – l'exception semble très générique et aucun problème analogue trouvé. Une des suggestions que j'ai trouvées était que mon développement Windows 7 pourrait ne pas être capable de gérer de nombreuses connections simultanées car ce n'est pas un operating system server. J'ai essayé d'installer notre application sur Windows 2008 Server – presque aucun changement de comportement, juste un peu plus de requests traitées avant qu'une exception ne se produise.

Code et commentaires supplémentaires sur la mise en œuvre

Les bases de données sont créées en utilisant une procédure stockée comme ceci:

CREATE PROCEDURE [dbo].[sp_DBCreate] ... @databasename nvarchar(124) -- 124 is max length of database file names AS DECLARE @sql nvarchar(150); BEGIN ... -- Create a new database SET @sql = N'CREATE DATABASE ' + quotename(@databasename, '['); EXEC(@sql); IF @@ERROR <> 0 RETURN -2; ... RETURN 0; END 

Les bases de données sont supprimées en utilisant le SP suivant:

 CREATE PROCEDURE [dbo].[sp_DomainDelete] ... @databasename nvarchar(124) -- 124 is max length of database file names AS DECLARE @sql nvarchar(200); BEGIN ... -- check if database exists IF EXISTS(SELECT * FROM [sys].[databases] WHERE [name] = @databasename) BEGIN -- drop all active connections SET @sql = N'ALTER DATABASE' + quotename(@databasename, '[') + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE'; EXEC(@sql); -- Delete database SET @sql = N'DROP DATABASE ' + quotename(@databasename, '['); EXEC(@sql); IF @@ERROR <> 0 RETURN -1; --error deleting database END --ELSE database does not exist. consider it deleted. RETURN 0; END 

Dans les deux PS, j'ai sauté des parties less pertinentes comme les controls de santé mentale.

Je n'utilise aucun ORM, tous les SP sont appelés à partir du code en utilisant des instances OdbcCommand . New OdbcConnection est créé pour chaque appel de fonction.

J'espère sincèrement que quelqu'un pourrait me donner une idée du problème.

UPD : Le même problème se produit si nous créons rapidement un tas de bases de données. Merci à tout le monde pour les suggestions sur le code de suppression de database, mais je préférerais avoir une solution ou au less un indice pour un problème plus général – celui qui se produit même sans supprimer les DB du tout.

UPD2 : Le code suivant est utilisé pour les appels SP:

 public static int ExecuteNonQuery(ssortingng sql, params object[] parameters) { try { var command = new OdbcCommand(); Prepare(command, new OdbcConnection( GetConnectionSsortingng() /*irrelevant*/), null, CommandType.Text, sql, parameters == null ? new List<OdbcParameter>().ToArray() : parameters.Select(p => p is OdbcParameter ? (OdbcParameter)p : new OdbcParameter(ssortingng.Empty, p)).ToArray()); return command.ExecuteNonQuery(); } catch (OdbcException ex) { // Logging here throw; } } public static void Prepare( OdbcCommand command, OdbcConnection connection, OdbcTransaction transaction, CommandType commandType, ssortingng commandText, params OdbcParameter[] commandParameters) { if (connection.State != ConnectionState.Open) { connection.Open(); } command.Connection = connection; command.CommandText = commandText; if (transaction != null) { command.Transaction = transaction; } command.CommandType = commandType; if (commandParameters != null) { command.Parameters.AddRange( commandParameters.Select(p => p.Value==null && p.Direction == ParameterDirection.Input ? new OdbcParameter(p.ParameterName, DBNull.Value) : p).ToArray()); } } 

Exemple de string de connection:

 Driver={SQL Server}; Server=LOCALHOST;Uid=sa;Pwd=<password here>; 

D'accord. Il peut y avoir des problèmes de scope pour OdbcConnection mais vous ne semblez pas non plus fermer les connections une fois que vous avez terminé avec eux. Cela peut signifier que vous dépendez du gestionnaire de pool pour fermer les connections inutilisées et les renvoyer au pool au fur et à mesure de leur expiration. Le bloc using fermera automatiquement et se débarrassera de la connection une fois terminé, ce qui permettra de le renvoyer au pool de connections.

Essayez ce code:

 public static int ExecuteNonQuery(ssortingng sql, params object[] parameters) { int result = 0; try { var command = new OdbcCommand(); using (OdbcConnection connection = new OdbcConnection(GetConnectionSsortingng() /*irrelevant*/)) { connection.Open(); Prepare(command, connection, null, CommandType.Text, sql, parameters == null ? new List<OdbcParameter>().ToArray() : parameters.Select(p => p is OdbcParameter ? (OdbcParameter)p : new OdbcParameter(ssortingng.Empty, p)).ToArray()); result = command.ExecuteNonQuery(); } } catch (OdbcException ex) { // Logging here throw; } return result; }