I'm trying to restore a .bak file on a remote sql server but i'm getting
Cannot open backup device .bak Operating system error 21(The device is not ready.). RESTORE DATABASE is terminating abnormally.
This is what i tried
var restore = new Restore();
restore.Database = databaseName;
restore.Action = RestoreActionType.Database;
restore.Devices.AddDevice(backUpFilePath, DeviceType.File);
restore.ReplaceDatabase = true;
restore.NoRecovery = false;
var sqlConnection = new SqlConnection(ConnectionString);
var serverConnection = new ServerConnection(sqlConnection);
var sqlServer = new Server(serverConnection);
restore.SqlRestore(sqlServer);
I believe the error message is quite vague, in that it can mean a number of different things.
Are you able to restore that .bak outside of your application in SQL Server? Do the SQL Error logs give any more specific information?
Related
I am restoring a full database backup from our production server to our staging server using the Restore class in SQL Management Objects, via a C# script task in SSIS. The database I am restoring is a CDC enabled database with a number of change tables - however these tables are being dropped once the restore has completed. I know that there is a KEEP_CDC option in the TSQL Restore command but I can't seem to find an equivalent property in SMO. Is there a way that I can do the restore using SMO whilst retaining the CDC capture instance? This is my code:
Server srv;
srv = new Server();
//kill all connections to the database prior to restore
srv.KillAllProcesses(databaseName);
Restore res = new Restore();
res.Database = databaseName;
res.Action = RestoreActionType.Database;
res.Devices.AddDevice(filePath, DeviceType.File);
res.ReplaceDatabase = true;
res.ReadFileList(srv);
DataTable filelist = res.ReadFileList(srv);
foreach (DataRow row in filelist.Rows)
{
string logicalFileName = Path.GetFileName(row["LogicalName"].ToString());
string physicalFileName = Path.GetFileName(row["PhysicalName"].ToString());
switch (Path.GetFileName(row["Type"].ToString()))
{
case "D":
res.RelocateFiles.Add(new RelocateFile(logicalFileName, Path.Combine(DataFilePath, physicalFileName)));
break;
case "L":
res.RelocateFiles.Add(new RelocateFile(logicalFileName, Path.Combine(LogFilePath, physicalFileName)));
break;
}
}
res.SqlRestore(srv);
Currently i am backing up a remote database via SMO. I am able to backup with DeviceType.File
I want to be able to use VirtualDevice:
Backup dbBackup = new Backup();
dbBackup.Action = BackupActionType.Database;
dbBackup.Database = "DevTarget";
BackupDeviceItem dbBackupItem = new BackupDeviceItem();
dbBackupItem.DeviceType = DeviceType.VirtualDevice;
dbBackupItem.Name = "Full_Backup_test2";
dbBackup.Devices.Add(dbBackupItem);
Has anybody ever used DeviceType.VirtualDevice if so please help as to how to use it and its features.
I'm using this function for restoring a SQL Server database.
It works perfectly, but my problem is when I am using this function, I have to make sure that the SQL Server connection is closed in my application.
If a SQL Server connection is open in other window this function will not work.
So, how can I restore my database even if a SQL Server connection is open in another window?
I mean is there any way to lock SQL Server during my restore function?
private void btnRestore_Click(object sender, EventArgs e)
{
// If there was a SQL connection created
try
{
if (srvSql != null)
{
saveBackupDialog.Title = "Restore Backup File";
saveBackupDialog.InitialDirectory = "D:";
// If the user has chosen the file from which he wants the database to be restored
if (openFD.ShowDialog() == DialogResult.OK)
{
Thread oThread = new Thread(new ThreadStart(frmWaitShow));
oThread.Start();
// Create a new database restore operation
Restore rstDatabase = new Restore();
// Set the restore type to a database restore
rstDatabase.Action = RestoreActionType.Database;
// Set the database that we want to perform the restore on
rstDatabase.Database = cmbDatabase.SelectedItem.ToString();
// Set the backup device from which we want to restore, to a file
BackupDeviceItem bkpDevice = new BackupDeviceItem(openFD.FileName, DeviceType.File);
// Add the backup device to the restore type
rstDatabase.Devices.Add(bkpDevice);
// If the database already exists, replace it
rstDatabase.ReplaceDatabase = true;
// Perform the restore
rstDatabase.SqlRestore(srvSql);
oThread.Suspend();
MessageBox.Show("DataBase Restore Successfull");
}
else
{
// There was no connection established; probably the Connect button was not clicked
MessageBox.Show("A connection to a SQL server was not established.", "Not Connected to Server", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
}
I've found killing all processes for the database, setting single user mode, and then detaching the database is effective and can be done with SMO. All three steps were necessary to cover different scenarios, though I couldn't tell you off hand what those are. Theoretically, only placing the DB in single-user mode is necessary.
// Kill all processes
sqlServer.KillAllProcesses(restore.Database);
// Set single-user mode
Database db = sqlServer.Databases[restore.Database];
db.DatabaseOptions.UserAccess = DatabaseUserAccess.Single;
db.Alter(TerminationClause.RollbackTransactionsImmediately);
// Detach database
sqlServer.DetachDatabase(restore.Database, false);
In your method:
private void btnRestore_Click(object sender, EventArgs e)
{
// If there was a SQL connection created
try
{
if (srvSql != null)
{
saveBackupDialog.Title = "Restore Backup File";
saveBackupDialog.InitialDirectory = "D:";
// If the user has chosen the file from which he wants the database to be restored
if (openFD.ShowDialog() == DialogResult.OK)
{
Thread oThread = new Thread(new ThreadStart(frmWaitShow));
oThread.Start();
// Create a new database restore operation
Restore rstDatabase = new Restore();
// Set the restore type to a database restore
rstDatabase.Action = RestoreActionType.Database;
// Set the database that we want to perform the restore on
rstDatabase.Database = cmbDatabase.SelectedItem.ToString();
// Set the backup device from which we want to restore, to a file
BackupDeviceItem bkpDevice = new BackupDeviceItem(openFD.FileName, DeviceType.File);
// Add the backup device to the restore type
rstDatabase.Devices.Add(bkpDevice);
// If the database already exists, replace it
rstDatabase.ReplaceDatabase = true;
// Kill all processes
srvSql.KillAllProcesses(rstDatabase.Database);
// Set single-user mode
Database db = srvSql.Databases[rstDatabase.Database];
db.DatabaseOptions.UserAccess = DatabaseUserAccess.Single;
db.Alter(TerminationClause.RollbackTransactionsImmediately);
// Detach database
srvSql.DetachDatabase(rstDatabase.Database, false);
// Perform the restore
rstDatabase.SqlRestore(srvSql);
oThread.Suspend();
MessageBox.Show("DataBase Restore Successfull");
}
else
{
// There was no connection established; probably the Connect button was not clicked
MessageBox.Show("A connection to a SQL server was not established.", "Not Connected to Server", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
}
Well you need to close all active connections before beginning your process of restoring. As it will fail if any of the user is connected to your database.
You can achieve that by using the following script and executing it before restore.
use master
ALTER DATABASE YourDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE
--do you stuff here
ALTER DATABASE YourDatabase SET MULTI_USER
In SMO, there's a KillAllProcesses method on the Server object. It takes a database as the sole argument.
I will try to do a backup database from C# application. I found more tutorials how do that. In the new project copy some solution and run. All the time I got one connection error like:
"backup failed for server ".
In this line:
source.SqlBackup(server);
Do you know how I resolve this? I think that problem concerns connection to server (it's broken?).
Below you can see a Backup method:
public static void BackupDatabase(string backUpFile)
{
ServerConnection con = new ServerConnection(#".\SQLEXPRESS");
Server server = new Server(con);
Backup source = new Backup();
source.Action = BackupActionType.Database;
source.Database = "DB";
BackupDeviceItem destination = new BackupDeviceItem(backUpFile, DeviceType.File);
source.Devices.Add(destination);
source.SqlBackup(server);
con.Disconnect();
MessageBox.Show("Kopia wykonana!");
}
Couple of things for you to try.
Make sure your database name is correct
source.Database = "DB"; // Check the database name is actually 'DB'.
I had some issues in the past using ServerConnection with a connection string, even though the syntax allows you to do so. What i did was to create an SqlConnection from the connection string and then give that to ServerConnection.
string connectionString = "Your connection string goes here";
SqlConnection sqlCon = new SqlConnection(connectionString);
ServerConnection connection = new ServerConnection(sqlCon);
I would also try initializing the backup object.
source.Initialize = true;
Added full control for PC Users to the backup folder on C:\ drive helped! Thanks all for help! But just I have one question: how I can modify above C# code that program should be yourself create backup folder on C:\ and do a copy database? Currently I must do it manually.
I am trying to make a copy of a database to a new database on the same server. The server is my local computer running SQL 2008 Express under Windows XP.
Doing this should be quite easy using the SMO.Transfer class and it almost works!
My code is as follows (somewhat simplified):
Server server = new Server("server");
Database sourceDatabase = server.Databases["source database"];
Database newDatbase = new Database(server, "new name");
newDatbase.Create();
Transfer transfer = new Transfer(sourceDatabase);
transfer.CopyAllObjects = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newDatbase.Name;
transfer.CopySchema = true;
transfer.CopyData = true;
StringCollection transferScript = transfer.ScriptTransfer();
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand switchDatabase = new SqlCommand("USE " + newDatbase.Name, conn))
{
switchDatabase.ExecuteNonQuery();
}
foreach (string scriptLine in transferScript)
{
using (SqlCommand scriptCmd = new SqlCommand(scriptLine, conn, transaction))
{
int res = scriptCmd.ExecuteNonQuery();
}
}
}
What I do here is to first create a new database, then generate a copy script using the Transfer class and finally running the script in the new database.
This works fine for copying the structure, but the CopyData option doesn't work!
Are there any undocumented limits to the CopyData option? The documentation only says that the option specifies whether data is copied.
I tried using the TransferData() method to copy the databse without using a script but then I get an exception that says "Failed to connect to server" with an inner exception that says "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)"
I also tried to enable Named Pipes on the server, but that doesn't help.
Edit:
I found a solution that works by making a backup and then restoring it to a new database. It's quite clumsy though, and slower than it should be, so I'm still looking for a better solution.
Well, after contacting Microsft Support I got it working properly, but it is slow and more or less useless. Doing a backup and then a restore is much faster and I will be using it as long as the new copy should live on the same server as the original.
The working code is as follows:
ServerConnection conn = new ServerConnection("rune\\sql2008");
Server server = new Server(conn);
Database newdb = new Database(server, "new database");
newdb.Create();
Transfer transfer = new Transfer(server.Databases["source database"]);
transfer.CopyAllObjects = true;
transfer.CopyAllUsers = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newdb.Name;
transfer.DestinationServer = server.Name;
transfer.DestinationLoginSecure = true;
transfer.CopySchema = true;
transfer.CopyData = true;
transfer.Options.ContinueScriptingOnError = true;
transfer.TransferData();
The trick was to set the DestinationDatabase property. This must be set even if the target is that same as the source. In addition I had to connect to the server as a named instance instead of using the other connection options.
Try setting SetDefaultInitFields to true on the Server object.
I had the same issue with the SMO database object running slowly. I guess this is because sql server doesn't like to retrieve entire objects and collections at once, instead lazy loading everything, causing a round-trip for each field, which for an entire database is pretty inefficient.
I had a go at getting this working and have come up with an answer that doesn't use the Transfer class. Here is the Method i used:
public bool CreateScript(string oldDatabase, string newDatabase)
{
SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=" + newDatabase + ";User Id=sa;Password=sa;");
try
{
Server sv = new Server();
Database db = sv.Databases[oldDatabase];
Database newDatbase = new Database(sv, newDatabase);
newDatbase.Create();
ScriptingOptions options = new ScriptingOptions();
StringBuilder sb = new StringBuilder();
options.ScriptData = true;
options.ScriptDrops = false;
options.ScriptSchema = true;
options.EnforceScriptingOptions = true;
options.Indexes = true;
options.IncludeHeaders = true;
options.WithDependencies = true;
TableCollection tables = db.Tables;
conn.Open();
foreach (Table mytable in tables)
{
foreach (string line in db.Tables[mytable.Name].EnumScript(options))
{
sb.Append(line + "\r\n");
}
}
string[] splitter = new string[] { "\r\nGO\r\n" };
string[] commandTexts = sb.ToString().Split(splitter, StringSplitOptions.RemoveEmptyEntries);
foreach (string command in commandTexts)
{
SqlCommand comm = new SqlCommand(command, conn);
comm.ExecuteNonQuery();
}
return true;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("PROGRAM FAILED: " + e.Message);
return false;
}
finally
{
conn.Close();
}
}
Here is my solution:
I have a Database named is Olddatabase
I backup it to E:\databackup\Old.bak
If you want to create a Duplicate Database from Olddatabase in the same server with name NewDatabase
3.1 You can use command in query tool : EXEC OldDatabase.dbo.sp_helpfile;
to determinat path of OldDatabase is stored in case you want to save NewDatabase in the same folder.
or You can save NewDatabase in new Path which you want
use this command in Query tool
RESTORE DATABASE NewDatabase FROM DISK = 'E:\databackup\Old.bak'
WITH MOVE 'OldDatabase' TO 'E:\New path (or the same path)\NewDatabase_Data.mdf',
MOVE 'OldDatabase_log' TO 'E:\New path (or the same path)\NewDatabase_Log.ldf';
Note: you can Use these command obove in c# with : Create a Store procedure in sql which include Above commands. And you can call the store procedure in C #