Rollback transaction to savepoint on failing ALTER TABLE ... ADD CONSTRAINT - c#

Is there any way to add a check constraint in a transaction and in case of failure rolling back to a previous savepoint (instead of rolling back the entire transaction)?
In my case, when an ALTER TABLE ... ADD CONSTRAINT command fails, the transaction cannot be rolled back to the savepoint (the attempt to do so throws an InvalidOperationException).
Overview to demonstrate the crucial point:
SqlTransaction transaction = connection.BeginTransaction();
// ... execute SQL commands on the transaction ...
// Create savepoint
transaction.Save("mySavepoint");
try
{
// This will fail...
SqlCommand boom = new SqlCommand(
"ALTER TABLE table WITH CHECK ADD CONSTRAINT ...",
connection,
transaction);
boom.ExecuteNonQuery();
}
catch
{
// ...and should be rolled back to the savepoint, but can't.
try
{
transaction.Rollback("mySavepoint");
}
catch (InvalidOperationException)
{
// Instead, an InvalidOperationException is thrown.
// The transaction is unusable and can only be rolled back entirely.
transaction.Rollback();
}
}
And here's ready-to-run demo code to test (you need a datase named "test"):
public class Demo
{
private const string _connectionString = "Data Source=(local);Integrated security=true;Initial Catalog=test;";
private const string _savepoint = "save";
private static readonly string _tableName = DateTime.Now.ToString("hhmmss");
private static readonly string _constraintName = "CK" + DateTime.Now.ToString("hhmmss");
private static readonly string _createTable = "CREATE TABLE [dbo].[" + _tableName + "] ([one] [int] NULL,[two] [int] NULL) ON [PRIMARY]";
private static readonly string _insert1 = "INSERT INTO [" + _tableName + "] VALUES (1,1)";
private static readonly string _addConstraint = "ALTER TABLE [dbo].[" + _tableName + "] WITH CHECK ADD CONSTRAINT [" + _constraintName + "] CHECK (([one]>(1)))";
private static readonly string _insert2 = "INSERT INTO [" + _tableName + "] VALUES (2,2)";
public static void Main(string[] args)
{
// Example code! Please ignore missing using statements.
SqlConnection connection = new SqlConnection(_connectionString);
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
SqlCommand createTable = new SqlCommand(_createTable, connection, transaction);
createTable.ExecuteNonQuery();
// Create savepoint
transaction.Save(_savepoint);
SqlCommand insert1 = new SqlCommand(_insert1, connection, transaction);
insert1.ExecuteNonQuery();
try
{
// This will fail...
SqlCommand boom = new SqlCommand(_addConstraint, connection, transaction);
boom.ExecuteNonQuery();
}
catch
{
// ...and should be rolled back to the savepoint, but can't
transaction.Rollback(_savepoint);
}
SqlCommand insert2 = new SqlCommand(_insert2, connection, transaction);
insert2.ExecuteNonQuery();
transaction.Commit();
connection.Close();
}
}

I get the same behaviour when I tried in TSQL.
BEGIN TRAN
CREATE TABLE foo (col int)
INSERT INTO foo values (1)
SAVE TRANSACTION ProcedureSave;
BEGIN TRY
ALTER TABLE foo WITH CHECK ADD CONSTRAINT ck CHECK (col= 2)
END TRY
BEGIN CATCH
SELECT XACT_STATE() AS XACT_STATE
/*Returns -1, transaction is uncommittable. Next line will fail*/
ROLLBACK TRANSACTION ProcedureSave
/*Msg 3931, Level 16, State 1: The current transaction cannot be committed and
cannot be rolled back to a savepoint. Roll back the entire transaction.*/
END CATCH
GO
SELECT ##TRANCOUNT AS [##TRANCOUNT] /*Zero the transaction was rolled back*/
I didn't find any information in the docs that states which errors would lead to the transaction becoming doomed in this way. I think no such documentation exists from this connect item comment.
The answer is, the error handling is
case-by-case. It depends on not only
the serverity, but also the error type
and context. Unfortunately, there is
no published list of error handling
behavior for different errors. In
general, only servere errors should
kill the connection and extremely ones
shutdown server. But when it comes to
statement abort vs transaction abort,
it is hard to summarize the rules --
i.e. it is case-by-case.

I don't think you can intermingle save point usage in scripts and in C#. I perform the following SQL:
BEGIN TRANSACTION
INSERT INTO Foos (Fooname)
VALUES ('Bar1')
SAVE TRANSACTION MySavePoint;
INSERT INTO Foos (FooName)
VALUES ('Bar2')
ROLLBACK TRANSACTION MySavePoint
COMMIT TRANSACTION
This will work in SQL, and will work with the following code:
using (SqlConnection conn = new SqlConnection("connectionString"))
{
conn.Open();
using (SqlTransaction trans = conn.BeginTransaction())
using (SqlCommand comm = new SqlCommand("The Above SQL", conn, trans))
{
comm.ExecuteNonQuery();
trans.Commit();
}
}
If you attempt to trans.Rollback("MySavePoint"); it will fail because the trans object is not in control of the save point - it doesn't know about it.
If you split the SQL out into the two independent inserts and use the following code:
using (SqlConnection conn = new SqlConnection("connectionString"))
{
conn.Open();
using (SqlTransaction trans = conn.BeginTransaction())
using (SqlCommand comm1 = new SqlCommand("INSERT INTO Foos(fooName) VALUES('Bar1')", conn, trans))
using (SqlCommand comm2 = new SqlCommand("INSERT INTO Foos(fooName) VALUES('Bar2')", conn, trans))
{
comm1.ExecuteNonQuery();
trans.Save("MySavePoint");
comm2.ExecuteNonQuery();
trans.Rollback("MySavePoint");
trans.Commit();
}
}
It will work as you expect.
Just a note, always dispose of objects that implement IDisposable - preferably in a using statement.
Further reading:
http://www.davidhayden.com/blog/dave/archive/2005/10/15/2517.aspx
Update: after faffing with this for a while using your sample code, it looks like due to the error coming from SQL, the transaction is being rolled back and becomes unusable. As has been stated in another answer, it appears as though in conjunction with SQL, the transaction is being forcefully rolled back regardless of savepoints due to certain errors. The only recourse for this is to re-order the commands run against the database and not rely on savepoints, or at least not rely on that action being in a savepoint.

Related

SQL Server : "require open and available connection"

I'm just learning databases to store a (large amount) of user entry data.
I have the following code, which checks a record and chooses whether to update or create new
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sc1 = #"select count(*) from job1 where report = #report";
SqlCommand check = new SqlCommand(sc1, connection);
check.Parameters.AddWithValue("#report", jname);
// check if the report number already exists, if not make a new table otherwise insert
int test = (int)check.ExecuteScalar();
if (test > 0)
{
jobCardExistingTable(connection);
digCardExistingTable(connection);
//insert into existing table code
}
If I use either jobCardExistingTable or digCardExisting table, they work fine. If I use both, I get the error
require open and available connection
I assume that the first ExecuteNonQuery (which are contained in the jobCard and digCard methods) is doing something with the connection - can I keep this one open, or do I have to open a new one each time I call a method? Maybe I'm doing this all wrong anyways...each method is calling a new table in the database, should I be calling them all at once?
Edit: part of the issue is jobCardTable (digCardTable is identical, just a different query)
public void jobCardNewTable(SqlConnection connection)
{
using (connection)
{
string sc3 = "";
sc3 = #"INSERT INTO job1 (" + pv.jobstring + ") VALUES (" + pv.jobparam + ")";
SqlCommand cmd = new SqlCommand(sc3, connection);
queryParams(cmd, 0);
cmd.ExecuteNonQuery();
}
}
Edit: solved - realised that using{} disposes the connection. Took all the using{} out of the methods, and used a single using{} to encompass all the method calls and it works
You should not use using (connection) if you are using same connection in other part of code. using dispose connection and make unavailable for further connection.
So, your jobCardNewTable method implementation should be without using statement :
public void jobCardNewTable(SqlConnection connection)
{
string sc3 = "";
sc3 = #"INSERT INTO job1 (" + pv.jobstring + ") VALUES (" + pv.jobparam + ")";
SqlCommand cmd = new SqlCommand(sc3, connection);
queryParams(cmd, 0);
cmd.ExecuteNonQuery();
}
I would recommend to create new connection whenever you need it and dispose it.

Cannot add data into database in visual c#

As a beginner to c#, and I actaully spent a lot of time researching this:
I cannot add some data into the database, I can extract data from it, but cannot add anything into the database. I use sql server as my database.
try {
fname = fname_tb.Text;// first name
sname = sname_tb.Text; // second name
q = "insert into beforebath1(firstname,secondname) values(#fname,#sname)";
conn_string = Properties.Settings.Default.beforebath_connection_string;
SqlConnection co = new SqlConnection(conn_string);
SqlCommand cmd;
co.Open();
cmd = new SqlCommand(q, co);
cmd.Connection = co;
cmd.Parameters.AddWithValue("#fname", fname_tb.Text);
cmd.Parameters.AddWithValue("#sname", sname_tb.Text);
cmd.ExecuteNonQuery();
co.Close();
}
catch(Exception err) {
MessageBox.Show(err.toString());
}
my sql connection string is this:
Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\beforebath_db.mdf;Integrated Security=True;Connect Timeout=30
It is automatically generated when I created the database. Please help me insert the text in the two textboxes (fname_tb.Text and sname_tb.Text) into the table called beforebath1 of the database called beforebath_db.mdf.
Is it something to do with my data directory?
I see a couple of mistakes in your code.
First, why catch an exception that will only be shown in a message?
It is often best to let the exception bubble up to have the stack trace in debug. This is not the same if this is production code, which I doubt.
Second, make sure to dispose your objects adequately.
The Using Statement is the most prefered way to work with disposeable items such as a database connection and a command.
using (var cnx = new SqlConnection(connectionString)) {
cnx.Open();
var sql = #"insert into beforebath1 (first_name, second_name)
values (#fname, #lname)";
using (var cmd = new SqlCommand(sql, cnx)) {
cmd.Parameters.AddWithValue("#fname", fname_tb.Text);
cmd.Parameters.AddWithValue("#lname", lname_tb.Text);
try {
int rowsAffected = cmd.ExecuteNonQuery();
if (0 < rowsAffected) MessageBox.Show("Success!");
else MessageBox.Show("Failed!");
} catch (SqlException ex) {
// It is almost prefered to let the exception be thrown freely
// so that you may have its full stack trace and have more
// details on your error.
MessageBox.Show(ex.Message);
} finally {
if (cnx.State == ConnectionState.Open) cnx.Close();
}
}
}
This way, wrapping your disposable objects within using blocks, you make sure that everything is getting to get disposed automatically when exiting the code block.
As for your "it doesn't work" problem, I guess the problem be either at the connection string level, or at your table_name level.
You wish to insert into beforebath1, and your insert statement states table_name. Make sure you put the right table name where it belongs so that it may work properly.
Can you change you connection string to this:
Server=(LocalDB)\v11.0;Database=beforebath_db;Trusted_Connection=True;
This means the your app and other programs using the Db will all share the same instance.
Also, as mentioned by #Will, you should wrap your SQLConnection in a using statement for garbage collection.
For better implementations you can use stored_procedures like bellow:
Step1: Declare Stored Procedure for your Query:
CREATE PROCEDURE [dbo].[ADD_TO_BEFORE_PATH_SP]
/*Type of this variables should be their column types*/
#fname varchar(MAX),
#lname varchar(MAX)
AS
BEGIN
INSERT INTO [dbo].[beforebath1] (fname, lname)
VALUES (#fname,#lname)
END
Step2: Using Stored Procedure where you need:
SqlConnection con = new SqlConnection(connectionString);
SqlCommand com = new SqlCommand("ADD_TO_BEFORE_PATH_SP", con);
com.Parameters.AddWithValue("#fname", fname_tb.Text);
com.Parameters.AddWithValue("#lname", lname_tb.Text);
com.CommandType = CommandType.StoredProcedure;
try
{
con.Open();
com.ExecuteNonQuery();
}
catch (Exception)
{
throw;
}
finally
{
if (con.State == ConnectionState.Open)
con.Close();
}

If insert fails for one record delete all previously inserted row, is is possible in a transaction?

I have method in my code like SaveListOfObjects which I execute inside foreach loop and then insert records to SQL Server.
It works great when there is no error in data I am inserting. But if error occured then only valid data is inserted in SQL.
I want to do following:
Insert all record only in case that whole data is valid, and if one error occurred in inserting.
Delete all previously saved data in SQL.
So, I already tried with TransactionScope and SqlTransaction classes and even with SQL TRANSACTION but only thing I could manage is insert valid data and non-valid data was omitted.
Now, as far as I search on web, I found that parallel transaction is not possible. Also, SQL has Isolation Level which prohibited parallel tasks.
Is there any possible way to accomplish insert in SQL like ALL or NOTHING?
UPDATE:
My code is as following:
public int Ramiz_SavePack(IPacking pack)
{
using (var conn = (SqlConnection)connector.GetConnection())
{
conn.Open();
SqlTransaction transaction;
var comm = (SqlCommand)connector.GetCommand("Ramiz_Pack_Save");
comm.CommandType = CommandType.StoredProcedure;
transaction = conn.BeginTransaction();
comm.Transaction = transaction;
int rowNum = 0;
try
{
if (!string.IsNullOrEmpty(pack.BrojKolete))
comm.Parameters.Add("#BrojKolete", SqlDbType.NVarChar).Value = pack.BrojKolete;
else
comm.Parameters.Add("#BrojKolete", SqlDbType.NVarChar).Value = DBNull.Value;
comm.Parameters.Add("#Bosanski", SqlDbType.NVarChar).Value = pack.Bosanski;
comm.Parameters.Add("#Kom", SqlDbType.Float).Value = pack.Kom;
comm.Parameters.Add("#Vrsta", SqlDbType.NVarChar).Value = pack.Vrsta;
comm.Parameters.Add("#Datum", SqlDbType.Date).Value = pack.Datum;
comm.Parameters.Add("#BrojKamiona", SqlDbType.Int).Value = pack.BrojKamiona;
rowNum = comm.ExecuteNonQuery();
transaction.Commit();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
try
{
conn.Close();
transaction.Rollback();
}
catch (Exception ex2)
{
Console.WriteLine(ex2.Message);
}
}
return rowNum;
}
}
and calling this method inside this:
var pack = new Pack();
for (int i = 1; i < lastRow; i++)
{
pack.Ramiz_SavePack(new Packing
{
BrojKolete = Convert.ToString(brojKoleteRange.Offset[i, 0].Value2),
Bosanski = Convert.ToString(nazivArtiklaRange.Offset[i, 0].Value2),
Kom = Convert.ToDouble(komRange.Offset[i, 0].Value2),
Vrsta = Convert.ToString(vrstaRange.Offset[i, 0].Value2),
BrojKamiona = int.Parse(ddlBrojKamiona.SelectedItem.Value),
Datum = Convert.ToDateTime(txtDate.Text)
});
pnlMessageSuccess.Visible = true;
}
It looks to me like you are looping and calling the save method for each object. This isn't a problem if the transaction exists around that loop, but it doesn't. You are rollback/committing each object separately
You need to either create a list of objects to save and send it into the save method, or create a transaction that wraps the loop e.g.:
var list = new List<Pack>();
foreach(<your loop>)
{
list.Add(new Pack(<some values>);
}
SavePacks(list);
void SavePacks(IList<Pack> items)
{
<create connection and transaction here and loop through inserting each item, rollback on error>
}
or
using(var tran = new SqlTransaction())
{
<Do save logic here for all items and rollback if something went wrong>
}
Your problem lies in the fact that you open and close the transaction for every object that you write in the database. Of course this means that when you hit a problem with your data, the previous correct one has already written to the database and committed. So it cannot be rolled back.
The solution is to open the connection and the transaction outside your data insert method and pass these object instances inside the method.
public int Ramiz_SavePack(IPacking pack, SqlConnection conn, SqlTransaction transaction)
{
var comm = (SqlCommand)connector.GetCommand("Ramiz_Pack_Save");
comm.Connection = conn;
comm.CommandType = CommandType.StoredProcedure;
comm.Transaction = transaction;
....
}
.....
try
{
using (var conn = (SqlConnection)connector.GetConnection())
{
conn.Open();
SqlTransaction transaction = conn.BeginTransaction();
var pack = new Pack();
for (int i = 1; i < lastRow; i++)
{
pack.Ramiz_SavePack(new Packing
{
BrojKolete = Convert.ToString(brojKoleteRange.Offset[i, 0].Value2),
Bosanski = Convert.ToString(nazivArtiklaRange.Offset[i, 0].Value2),
Kom = Convert.ToDouble(komRange.Offset[i, 0].Value2),
Vrsta = Convert.ToString(vrstaRange.Offset[i, 0].Value2),
BrojKamiona = int.Parse(ddlBrojKamiona.SelectedItem.Value),
Datum = Convert.ToDateTime(txtDate.Text)
}, conn, transaction);
}
pnlMessageSuccess.Visible = true;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
transaction.Rollback();
}
In this way the method that inserts data, when it fails, will raise an exception trapped by the calling code that has opened the connection and the transaction. This calling code could then call the rollback method to undo every object inserted so far.
It is easier to make constraints on the data entry objects like constraining a certain Textbox to accept only numbers or whatever you would like .
And as another idea .. try checking the data Accuracy before Querying it to the database
One way is to create a DataTable from your list by mapping each object in the list to a row of the DataTable. Then you can loop through the DataTable rows to perform the insert operation, and include this code block inside a Transaction. This will allow you to prevent entering partial data.
You may refer to this link to get an idea how to do the list to DataTable conversion.

Getting timeout errors with SqlTransaction on same table

public TransImport()
{
ConnString = ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
SqlConnection conn_new;
SqlCommand command_serial_new;
SqlConnection conn;
SqlCommand command_serial;
SqlTransaction InsertUpdateSerialNumbers;
conn = new SqlConnection(ConnString);
command_serial = conn.CreateCommand();
conn_new = new SqlConnection(ConnString);
command_serial_new = conn_new.CreateCommand();
command_serial_new.CommandText = "SELECT 1 FROM YSL00 WHERE SERLNMBR = #slnr";
var p = new SqlParameter("#slnr", SqlDbType.NVarChar, 50);
command_serial_new.Parameters.Add(p);
//Here you will start reading flat file to get serialnumber.
InsertUpdateSerialNumbers = conn.BeginTransaction();
while (!headerFileReader.EndOfStream)
{
headerRow = headerFileReader.ReadLine();
if (CheckSerialNumber(headerFields[0].Trim()))
DisplayMessage("Good serialnumber"); //this function is not copied here.
}
InsertUpdateSerialNumbers.Commit();
}
private Boolean CheckSerialNumber(string SerialNumber)
{
command_serial_new.Parameters["#slnr"].Value = SerialNumber;
try
{
var itExists = Convert.ToInt32(command_serial_new.ExecuteScalar()) > 0;
if (!itExists)
{
command_serial.Transaction = InsertUpdateSerialNumbers;
command_serial.CommandText = "INSERT INTO YSL00([Manifest_Number],[PONUMBER],[ITEMNMBR],[SERLNMBR]"
+ "VALUES ('" + Manifest + "','" + PONr + "','" + itemNumber + "','" + serialNr + "')";
var insertStatus = command_serial.ExecuteNonQuery();
return true;
}
}
catch (Exception ex)
{
LogException(ex, "Error in CheckSerialNumber =>"+ command_serial_new.CommandText.ToString());
}
return false;
}
I get error "Timeout expired. The timeout period elapsed prior to completion of the operation or server is not responding".
The CheckSerialNumber function also does an insert to YSL00 (the same table where I had executescalar. See code above).
As I mentioned earlier there are 1000s of line in a flat file that I read and update YSL000 table.
Note that I have two separate sqlcommands and also two separate connections to handle this. Reason is with sqltransaction it doesn't let me to query on the same table. I think timeout may be happening because of this?
Thanks for reading. Please suggest
Update 1: Since I have not pasted entire code, I want to mention that dispose is done using below code in the program.
if (conn != null)
{
conn.Close();
conn.Dispose();
}
if (conn_new != null)
{
conn_new.Close();
conn_new.Dispose();
}
you can increase the time out of your SqlConnection object.
you can do this with your ConnString:
string connStr = "Data Source=(local);Initial Catalog=AdventureWorks;Integrated
Security=SSPI;Connection Timeout=300";
I think default isolation level - read commited - is preventing your 'CheckSerialNumber' method from being effective. Command_serial_new will not take into consideration rows inserted in your loop - this might lead to some troubles. To be honest I would also look for some deadlock. Perhaps command_serial_new is actually completely blocked by the other transaction.
To start off:
Set command_serial_new query as:
SELECT 1 FROM YSL00 WITH (NOLOCK) WHERE SERLNMBR = #slnr
Think about using lower isolation level to query inserted rows as well (set it to read uncommited).
Close your connections and transactions.
Use just one SqlConnection - you don't need two of them.
Many of the objects you are using implement IDisposable, and you should be wrapping them with using statements. Without these using statements, .NET won't necessarily get rid of your objects until an undetermined time when the garbage collector runs, and could block subsequent queries if it's still holding a transaction open somewhere.
So for example, you'll need to wrap your connections with using statements:
using (conn_new = new SqlConnection(ConnString)) {
...
If I am not mistaken you need to merge the file content with the table content.
For this purpose I would recommend you
Copy the file content in to a temporary table (see temporary tables and BulkInsert)
Use command MERGE (http://msdn.microsoft.com/en-us/library/bb510625.aspx) to merge the temporary table content with the original table

Dropping SQL Server database through C#

I am using this code to delete a database through C#
Int32 result = 0;
try
{
String Connectionstring = CCMMUtility.CreateConnectionString(false, txt_DbDataSource.Text, "master", "sa", "happytimes", 1000);
SqlConnection con = new SqlConnection();
con.ConnectionString = Connectionstring;
String sqlCommandText = "DROP DATABASE [" + DbName + "]";
if (con.State == ConnectionState.Closed)
{
con.Open();
SqlConnection.ClearPool(con);
con.ChangeDatabase("master");
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
else
{
con.ChangeDatabase("master");
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
con.Close();
con.Dispose();
result = 1;
}
catch (Exception ex)
{
result = 0;
}
return result;
But I get an error
Database currently in use
Can anyone help?
Try this:
String sqlCommandText = #"
ALTER DATABASE " + DbName + #" SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [" + DbName + "]";
Also make sure that your connection string defaults you to the master database, or any other database other than the one you're dropping!
As an aside, you really don't need all of that stuff around your queries. The ConnectionState will always start off Closed, so you don't need to check for that. Likewise, wrapping your connection in a using block eliminates the need to explicitly close or dispose the connection. All you really need to do is:
String Connectionstring = CCMMUtility.CreateConnectionString(false, txt_DbDataSource.Text, "master", "sa", "happytimes", 1000);
using(SqlConnection con = new SqlConnection(Connectionstring)) {
con.Open();
String sqlCommandText = #"
ALTER DATABASE " + DbName + #" SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [" + DbName + "]";
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
result = 1;
Here is how you do it using Entity Framework version 6
System.Data.Entity.Database.Delete(connectionString);
You should take a look at SMO.
These allow you to manage all aspects of SQL Server from code, including deleting of databases.
The database object has a Drop method to delete database.
Create sqlconnection object for different database other than you want to delete.
sqlCommandText = "DROP DATABASE [DBNAME]";
sqlCommand = new SqlCommand(sqlCommandText , sqlconnection);
sqlCommand.ExecuteNonQuery();
In this case i would recommend that you take the database offline first... that will close all connections and etc... heres an article on how to do it: http://blog.sqlauthority.com/2010/04/24/sql-server-t-sql-script-to-take-database-offline-take-database-online/
Microsoft clearly states that A database can be dropped regardless of its state: offline, read-only, suspect, and so on. on this MSDN article (DROP DATABASE (Transact-SQL))
Connection pooling at a guess, use sql server's activity monitor to make sure though.
Pooling keeps connections to the database alive in a cache, then when you create a new one, if there's one in the cache it hands it back instead of instantiating a new one. They hang around for a default time, (2 minutes I think) if they don't get re-used in that time, then they killed off.
So as a first go connect straight to master, instead of using change database, as I suspect change database will simply swap connections in the pool.
Add a check routine for database in use (use a connection to master to do it!). You can force the database to be dropped anyway by first executing
ALTER DATABASE [MyDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
again from the connection to master!
However everybody else using the db, will no longer like you at all...
Just don't use DB name in connection string.
"Data Source=.\SQLEXPRESS;Integrated Security=True;"
I was having the same troubles as Anshuman...
By my testing of the code in question of Anshuman there have been very simple error:
there have to be SqlConnection.ClearAllPools(); instead of SqlConnection.ClearPool(con);
Like this trouble of
"cannot drop database because is in use..."
disappears.

Categories

Resources