How do I disconnect open connections to an Azure SQL Managed Instance? - c#

I am working on a process for restoring a database to my Azure SQL Managed instance which involves dropping the existing database and restoring a backup in its place (since Managed Instance doesn't support WITH REPLACE).
I am, however, running into an issue with disconnecting any open connections so I can do this operation. Users should be warned before updates take place, but we cannot guarantee no open connections.
Typically, I would do something along the lines of the following:
ALTER DATABASE AdventureWorks2012 SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
--RESTORE HERE
ALTER DATABASE AdventureWorks2012 SET MULTI_USER;
When I attempt to run something like this against the database in the managed instance, however, I get the following error:
This ALTER DATABASE statement is not supported. Correct the syntax and execute the statement again. ALTER DATABASE statement failed.
Is there a way to accomplish this in an Azure SQL Managed Instance?

In RESTORE FROM URL in Managed Instance you can't even replace existing databases.
So either drop or rename the database before the RESTORE. Both DROP DATABASE MyDb and ALTER DATABASE MyDb MODIFY NAME = MyDb_old will kill existing connections to the database.
Alternatively you can use the Managed Point-in-Time Restore to restore an existing database to a previous point-in-time.

Related

C# localdb SHRINKDATABASE command from C# code

I'm trying to shrink a LocalDb with Visual Studio 2017 Community. I have a Win7 client windows form application with a small database (~10MB of data) that results into 150MB database size due to LocalDb free space allocation.
I found this answer (Executing Shrink on SQL Server database using command from linq-to-sql) that suggest to use the following code:
context.Database.ExecuteSqlCommand(
"DBCC SHRINKDATABASE(#file)",
new SqlParameter("#file", DatabaseTools.Instance.DatabasePathName)
);
DatabaseTools.Instance.DatabasePathName returns the filesystem location of my database from a singleton DatabaseTools class instance.
The code runs, but I keep getting this exception:
System.Data.SqlClient.SqlException: 'Cannot perform a shrinkdatabase operation inside a user transaction. Terminate the transaction and reissue the statement.'
I tried COMMIT before, but no success at all. Any idea on how to effectively shrink database from C# code?
Thanks!
As the docs for ExecuteSqlCommand say, "If there isn't an existing local or ambient transaction a new transaction will be used to execute the command.".
This is what's causing your problem, as you cannot call DBCC SHRINKDATABASE in a transaction. Which isn't really surprising, given what it does.
Use the overload that allows you to pass a TransactionalBehavior and specify TransactionalBehavior.DoNotEnsureTransaction:
context.Database.ExecuteSqlCommand(
TransactionalBehavior.DoNotEnsureTransaction,
"DBCC SHRINKDATABASE(#file)",
new SqlParameter("#file", DatabaseTools.Instance.DatabasePathName)
);

Have Sql SMO Delete Database Backup File

I have create a C# application that allows a database to be backed up and restored. It does this by first backing the database up to a local file on the Sql server using:
Backup backup = new Backup();
backup.Devices.AddDevice(Path.GetFullPath(backupFilePath), DeviceType.File);
...
backup.SqlBackup(server);
And then I create the new database by restoring from the backup file using:
Restore restore = new Restore();
restore.Devices.AddDevice(Path.GetFullPath(backupFileToRestoreFrom), DeviceType.File);
...
restore.SqlRestore(server);
After the new database has been created I want to delete the temp backup file that we created. Because I have admin rights on the Sql server box, I can delete the file on the remote server using:
File.Delete("\\SqlServer\C$\Backups\BackupFileToDelete.bak")
and it works. However, if somebody else who doesn't have rights on the Sql server box runs the app, it will throw an exception about not having permissions.
So is there a Sql SMO function that I can call to delete the backup file that was created on the remote Sql server?
If you delete the file using the xp_cmdshell then it will use the rights of the sql server instead of the rights of the person.
For example:
EXEC master..xp_cmdshell 'Del \\SqlServer\C$\Backups\BackupFileToDelete.bak', NO_OUTPUT
Here's the reference on xp_cmdshell
The important thing to note from that article is:
Because malicious users sometimes attempt to elevate their privileges
by using xp_cmdshell, xp_cmdshell is disabled by default. Use
sp_configure or Policy Based Management to enable it. For more
information, see xp_cmdshell Server Configuration Option.
Another possibility is to use SQLCLR, which is more secure than using xp_cmdshell. There's even a CodePlex project call SQLCLR File Functions that has the functionality written as stored procs for me. I think I'm going to see if I can convince our DBAs to go this route. The downside is that I believe the sprocs that this creates would need to be installed on every existing and new SQL Server that gets created; not much different than having to enable xp_cmdshell on every server though I guess.

Error in C# Code Backup option

I have wrote this code
but its generating error
please help
System.IO.File.Copy("JDB.mdf", "d:\\JDB.mdf", true);
System.IO.File.Copy("JDb_log.ldf", "d:\\JDb_log.ldf", true);
the error is
The process cannot access the file 'JDB.mdf' because it is being used by another process.
Please help me
The file is being used by some process. I believe the database is still attached to Sql Server.
Use Process Explorer tool from Sys Internal to find out the program using this file.
Why don't you use SQL back tool to backup the database?
Check out What happens during a live SQL Server backup?
If you still want to backup or make copy of ldf mdf files then you can perform these steps:
1. Detach database
USE MASTER;
GO
-- Take database in single user mode -- if you are facing errors
-- This may terminate your active transactions for database
ALTER DATABASE DatabaseName
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO
-- Detach DB
EXEC MASTER.dbo.sp_detach_db #dbname = N'DatabaseName'
Open Windows Explorer go to the the folder where ldf and mdf files are and then copy the file manually.
Reattach the database:
USE [master]
GO
CREATE DATABASE [DatabaseName] ON
( FILENAME = N’C:\Data\DataBase_Data.mdf’ ),
( FILENAME = N’C:\Data\Database_Log.ldf’ )
FOR ATTACH
GO
IF EXISTS ( SELECT name FROM master.sys.databases sd
WHERE name = N’DataBaseName’ AND SUSER_SNAME(sd.owner_sid) = SUSER_SNAME() )
EXEC [AdventureWorks].dbo.sp_changedbowner #loginame=N’sa’,#map=false
If you want to do it programatically, then you can execute the command given in step 1,2,3 using ADO.NET and the use c# file copy command to copy the file.
Download Process Explorer and run the program.
Option 1:
Click the Find menu, and choose Find Handle or DLL...
Type the file name (in your case JDB.mdf)
After typing the search phrase, click the Search button
Once you know what process the file has locked you, need to close that process (by closing that program). Another option is to use KILL in the process explorer terminating that process.
Option 2:
From the error message, the .mdf file has already been attached to an instance of an SQL Server. If you have multiple instances running, make sure it is detached from any other instance.

Trouble with duplicating a transaction's functionality

I'm updating a current program that is working and in use on a Live environment, it saves Customers and Orders then exports them to an old database as well. All of the Reporting is still done in the old system while the reporting system in the new system is in development, which is why these all need to be exported.
This program has a built-in C# TransactionManager that is used to group multiple calls from C# to SQL within one transaction. Whenever I try to duplicate this I get errors and can't get it working.
Here's the code that is in place, working:
using (ITransactionInfo trx = this.TransactionManager.BeginTransaction())
{
//
// Update the customer. If the customer doesn't exist, then create a new one.
//
this.SaveCustomer(Order);
//
// Save the Order.
//
this.Store.SaveCorporateOrder(Order, ServiceContext.UserId);
//
// Save the Order notes and the customer notes.
//
this.NotesService.AppendNotes(NoteObjectTypes.CorporateOrder, Order.Id, Order.OrderNotes);
this.NotesService.AppendNotes(NoteObjectTypes.Customer, Order.Customer.Id, Order.CustomerNotes);
//
// Export the Order if it's new.
//
this.ExportOrder(Order, lastSavedVersion);
//
// Commit the transaction.
//
trx.Commit();
}
All of these functions just format the data and send parameters to Stored Procedures in the DB that perform the Select / Insert / Update operations on the DB.
The SaveCustomer stored procedure saves the customer to the new database.
The SaveCorporateOrder stored procedure gets information that was writen by the Save Customer stored procedure and uses it to save the Order to the new database.
The ExportOrder stored procedure gets information that was written by both of the previous ones and exports the Order to the old database.
Each of these stored procedures contain code that starts a new transaction if ##TRANCOUNT == 0 and have a commit statement at the end. It appears that none of these are being used because of the transaction in C#, but there is no code that passes transaction information or connection information to the stored procedures that I can see. This is working and in use on a SQL 2005 server.
When I try to build this and use it on my development environment that uses SQL 2008R2, I get errors like
"Uncommittable transaction is detected at the end of the batch"
and
"The server failed to resume the transaction"
It appears that each one is starting it's own transaction and is unable to read the data from the previous, uncommitted transaction instead of seeing that it is in the same transaction. I don't know if the different SQL version could be causing this to work differently or not, but the exact same code works in the Live install but not on my Dev environment.
Any ideas, or even direction where to look next would be wonderfull!
Thanks!
-Jacob
I think that the problem is because transaction fails and is not rejected. You don't have rollback call for the situation when any of SQL queries fail. Have you checked those queries?

How do I run that database backup and restore scripts from my winform application?

I am developing a small business application which uses Sqlserver 2005 database.
Platform: .Net framework 3.5;
Application type: windows application;
Language: C#
Question:
I need to take and restore the backup from my application. I have the required script generated from SSME.
How do I run that particular script (or scripts) from my winform application?
You can run these scripts the same way you run a query, only you don't connect to the database you want to restore, you connect to master instead.
If the machine where your application is running has the SQL Server client tools installed, you can use sqlcmd.
If you want to do it programatically you can use SMO
Tutorial
Just use your connection to the database (ADO I presume?) and send your plain TSQL instructions to the server through this connection.
For the backup you probably want to use xp_sqlmaint. It has the handy ability to remove old backups, and it creates a nice log file. You can call it via something like:
EXECUTE master.dbo.xp_sqlmaint N''-S "[ServerName]" [ServerLogonDetails] -D [DatabaseName] -Rpt "[BackupArchive]\BackupLog.txt" [RptExpirationSchedule] -CkDB -BkUpDB "[BackupArchive]" -BkUpMedia DISK [BakExpirationSchedule]''
(replace the [square brackets] with suitable values).
Also for the backup you may need to backup the transaction log. Something like:
IF DATABASEPROPERTYEX((SELECT db_name(dbid) FROM master..sysprocesses WHERE spid=##SPID), ''Recovery'') <> ''SIMPLE'' EXECUTE master.dbo.xp_sqlmaint N'' -S "[ServerName]" [ServerLogonDetails] -D [DatabaseName] -Rpt "[BackupArchive]\BackupLog_TRN.txt" [RptExpirationSchedule] -BkUpLog "[BackupArchive]" -BkExt TRN -BkUpMedia DISK [BakExpirationSchedule]''
I'd recommend storing the actual commands you're using in a database table (1 row per command) and use some sort of template replacement scheme to handle the configurable values. This would allow for easy changes to the commands, without needing to deploy new code.
For the restore you will need to kill all connections except for internal sql server ones. Basically take the results of "exec sp_who" and for rows that match on dbname, and have a status that is not "background", and a cmd that is not one of "SIGNAL HANDLER", "LOCK MONITOR", "LAZY WRITER", "LOG WRITER", "CHECKPOINT SLEEP" do a "kill" on the spid (eg: ExecuteNonQuery("kill 1283")).
You'll want to trap and ignore any exceptions from the KILL command. There's nothing you can do about them. If the restore cannot proceed because of existing connections it will raise an error.
One danger with killing connections is ADO's connection pool (more for asp.net apps than windows apps). ADO assumes the a connection fetched from the connection pool is valid... and it does not react well to connections that have been killed. The next operation that occurs on that connection will fail. I can't recall the error... you might be able to trap just that specific error and handle it... also with 3.5 I think you can flush the connection pool (so... trap the error, flush the connection pool, open the connection, try the command again... ugly but might be doable).

Categories

Resources