Problem in Restoring a Database using C#.net Coding - c#

I have make Some Differential Backup for a Database, which will take the Backup for the Last Modified Data which will be appended to the previously happend Full Backup file. Now, when i am trying to take Restore of the .bak file entire Data is getting Backup, is it possible to take the Backup only the Last Backup taken data i wanted? Can, any one help me on this.
private void RestoreDataBase(Server MyServer, Database MyDataBase, string DevicePath, string Type)
{
try
{
progressBar1.Value = 0;
Restore restoreDB = new Restore();
restoreDB.Action = RestoreActionType.Database;
restoreDB.Database = MyDataBase.Name;
restoreDB.Devices.AddDevice(DevicePath, DeviceType.File);
restoreDB.ReplaceDatabase = true;
restoreDB.NoRecovery = true;
restoreDB.PercentComplete += new PercentCompleteEventHandler(rstDatabase_PercentComplete);
restoreDB.Complete += new ServerMessageEventHandler(rstDatabase_Complete);
restoreDB.SqlRestore(MyServer);
}
catch (Exception ex)
{
WriteToListView(ex.Message.ToString());
writetoLog(ex.Message.ToString());
}
}
The above is the coding i am using, and the database is Sql Server 2008, The coding i am using for Differential Backup is as follows
private void BackupDataBaseDifferential(Server MyServer, Database MyDataBase, string DestinationPath, string Type)
{
try
{
WriteToListView("Taking the Differential Backup for " + MyDataBase.Name);
Backup backDB = new Backup();
backDB.Action = BackupActionType.Database;
backDB.Database = MyDataBase.Name;
backDB.Devices.AddDevice(DestinationPath, DeviceType.File);
backDB.BackupSetName = "Sql Database Backup Differential";
backDB.BackupSetDescription = "Sql Database Backup - DifferentialType";
backDB.ExpirationDate = DateTime.Now.AddDays(5);
backDB.Initialize = false;
backDB.Incremental = true;
if (Type == "Manual")
{
progressBar1.Value = 0;
backDB.PercentComplete += new PercentCompleteEventHandler(bd_PercentComplete);
backDB.Complete += new ServerMessageEventHandler(bd_Complete);
}
else if (Type == "Automatic")
{
backDB.PercentComplete += CompletionStatusInPercent;
backDB.Complete += Backup_Completed;
}
backDB.SqlBackup(MyServer);
}
catch (Exception ex)
{
WriteToListView(ex.Message.ToString());
writetoLog(ex.Message.ToString());
}
}
So, if i use the above coding the last data which is modified in the Database (which was already taken Backup Fully previously) will be taken. So, my problem is when i use the RestoreDataBase() method the entire Database is getting restore, bcoz all the Full Backup data and the Differential Backup data will be in only one file ex: Sample.bak. If i want only the last modified data is it not possible to take by Specifying the Date of the Data modified to the Database?

Maybe silly, but if you export your db as .sql, when you need to restore only some data, you can read file and "filter" rows to restore...
Anyway there is another thread talking about this, take a look here.

Related

How to restore multiple transaction logs using SMO for .NET

I am working on a database application to restore a database with its transaction logs. I am using the SQL Server Management Objects (SMO) Library.
The requirements of this application make it so that I must restore the database backup file and its transaction logs in separate processes. I can restore the backup file without a hitch however, when it comes to restoring the transaction logs I come across a problem:
public void RestoreTransactionLogs(Server srv, DirectoryInfo filePath, DatabaseType dbType)
{
Restore res = new Restore()
{
Database = dbType.ToString(),
Action = RestoreActionType.Log,
ReplaceDatabase = false
};
FileInfo[] files = filePath.Parent.GetFiles("*.trn");
foreach (FileInfo f in files)
{
res.Devices.AddDevice(f.FullName, DeviceType.File);
}
try
{
res.SqlRestore(srv);
}
catch (SmoException ex)
{
Log.Fatal("An SMO Exception has occurred when restoring the database: " + dbType.ToString() + ": " + ex.Message);
throw ex;
}
catch (Exception ex)
{
Log.Fatal("An exception has occurred when restoring the database: " + dbType.ToString() + ": " + ex.Message);
throw ex;
}
}
Using a test backup file and 20 transaction logs, I run into the following error:
SmoException: System.Data.SqlClient.SqlError: The media loaded on
"D:\Test Folder\testDatabase\log_00001.trn" is formatted to support
1 media families, but 20 media families are expected according to the
backup device specification.
I have a feeling I am not adding the transaction logs to my device collection properly or I should be adding them in a different manner but I am unsure where to check. The documentation from MSDN for transaction logs is scarce and I haven't been able to find much online. Thanks!
I think your problem is that you can't restore just a transaction log. You have to start with a full backup first and then apply the transaction log(s). This must happen while db is in no recovery state and thus no other change to the db occurs between the full backup restore and the transaction log restore. Also, keep in mind that you have to apply transaction logs in the order they were taken.
Here is what I got working based on the doco example here:
https://learn.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/tasks/backing-up-and-restoring-databases-and-transaction-logs?view=sql-server-ver15
using (SqlConnection connection = new SqlConnection(connectionString))
{
var server = new Server(new ServerConnection(connection));
Database targetDb = server.Databases["TargetDbName"];
// Make sure your user has ALTER ANY CONNECTION rights for this
// not needed if you can be sure db is not in use
server.KillAllProcesses(targetDb.Name);
targetDb.SetOffline();
Restore restoreDB = new Restore();
restoreDB.Database = targetDb.Name;
restoreDB.Action = RestoreActionType.Database;
restoreDB.ReplaceDatabase = true;
// Restore the full backup first
var fullBackupDevice = new BackupDeviceItem("fullBackupFile.bak", DeviceType.File);
restoreDB.Devices.Add(fullBackupDevice);
restoreDB.NoRecovery = true;
restoreDB.SqlRestore(server);
restoreDB.Devices.Remove(fullBackupDevice);
// Get the first taken transaction log file
var firstTransactionBackupDevice = new BackupDeviceItem("firstTrnFile.trn", DeviceType.File);
restoreDB.Devices.Add(firstTransactionBackupDevice);
restoreDB.SqlRestore(server);
restoreDB.Devices.Remove(firstTransactionBackupDevice);
// Get the second taken transaction log file
var secondTransactionBackupDevice = new BackupDeviceItem("secondTrnFile.trn", DeviceType.File);
restoreDB.Devices.Add(secondTransactionBackupDevice);
// You have to set this flag to false before the last file you will restore
// to return the db to the normal state
restoreDB.NoRecovery = false;
restoreDB.SqlRestore(server);
restoreDB.Devices.Remove(secondTransactionBackupDevice);
targetDb.SetOnline();
server.Refresh();
}
I know your question is a bit older and you've probably found a solution already, but hopefully this helps to someone else.
Hi man you can try the scripts below within your sql server. Do change the directory file path. Also do your research on "with recovery" and "no recovery" .. example beow
RESTORE DATABASE [DW] FROM DISK = 'G:\MSSQL\Data\FullBackups\db.bak' WITH NORECOVERY
Go
RESTORE DATABASE [DW] FROM DISK = 'G:\MSSQL\Data\DifferentialBackups\db.bak' WITH NORECOVERY
--repeat how many ever times if multiple based on time DB crashed.
GO
RESTORE DATABASE [DW] FROM DISK = 'G:\MSSQL\Data\TransactionLogs\db.bak' WITH NORECOVERY
--Final T-Log to exact point in time recovery.
RESTORE DATABASE [DW] FROM DISK = 'G:\MSSQL\Data\TransactionLogs\db.bak' WITH RECOVERY

Restore Database from bak file

I'm trying to restore a database from a bak file. I found some code on how to do it grammatically but I'm not sure what I'm doing wrong. I'm getting an error:
Error:
Restore failed for Server 'www.freegamedata.com'.
I assume because i'm remotely connected? I'm not sure. The bak file is not on the server machine. I'm trying to build a desktop application that will install my database on the users server using my file. Here is my code:
private void Restore_Database()
{
try
{
Server server = new Server(Properties.Settings.Default.SQL_Server);
string filename = "Test.bak";
string filepath = System.IO.Directory.GetCurrentDirectory() + "\\file\\" + filename;
Restore res = new Restore();
res.Database = Properties.Settings.Default.SQL_Database;
res.Action = RestoreActionType.Database;
res.Devices.AddDevice(filepath, DeviceType.File);
res.PercentCompleteNotification = 10;
res.ReplaceDatabase = true;
res.PercentComplete += new PercentCompleteEventHandler(res_PercentComplete);
res.SqlRestore(server);
}
catch(Exception ex)
{
}
}
I'm not sure if I'm going about this the correct way. I'd like to add my database with my data to the users server as a base database. Am I doing something wrong? My connection string is good so I know its not a connection issue.
I have found a workaround for those whom do not have local access. This is a bit involved so I hope I explain this correctly and it makes sense.
Also note you will need to export your data to an excel spreadsheet before you do the steps listed below.
Exporting Data
Part 1:
Backup Your DATA!
This is a pretty simple process. Open SQL Management Studio and right click on your database. Choose export data and export it as an excel spreadsheet 2007. I'm not going to give detailed steps on this part because its pretty basic and you can google it. Sorry for the inconvenience.
Part 2:
Delete your database for testing purposes but make sure you have a working backup before you delete your database.
Importing Data
Part 1:
You need to create a script that will build your database for you automatically. You can do this by logging into SQL management Studio and right click on the database and choose:
Task -> Generate scripts
you should only need the default information. However, if your like me, I excluded the users in the list. This will generate a large SQL script.
Part 2:
Next you will want to store this file in your solution/project. Make sure you right click it and choose always copy or or copy if newer. I think that's the options. Basically it just copies your file when you debug or build it. This is critical because you will need to access this file to execute the script. Next you need to make a SQL function similar to mine to execute the script:
public bool SQLScript_ExecuteSQLScript(string ScriptLocation)
{
try
{
//5 min timeout
SqlConnection SQLConn = new SqlConnection(cn + "; Connection Timeout = 300;");
string script = File.ReadAllText(ScriptLocation);
Server server = new Server(new ServerConnection(SQLConn));
server.ConnectionContext.ExecuteNonQuery(script);
return true;
}
catch (Exception ex)
{
return false;
}
}
In my code sample please note I changed my timeout to 5 minutes. In the event you have a large script you may need to adjust the timeout to make sure your script fully executes.
Congrats you have rebuilt your database.
Part 3:
Load SQL Management Studio and make sure your database has been rebuilt successfully. You should see all your tables and Stored Procs but no data. If this is true, great you can continue. If not please go back and review your script. If you have SQL comments in your script, you may need to remove them. I had to in order for my script to execute without errors.
Part 4:
Now you need to import your data from your excel spreadsheet you created earlier. If your like me, you had multipal sheets. If you have multipal sheets then you will want to make a list to loop through each item in your list to import the sheets. If not then you can ignore my code on the list. I also put mine in a background worker but you don't need to depending on the size of your data. Also note I created a separate class containing my list but you dont have to do that if you don't want too. My sheet names are Table_1, Table_2 and Table_3 your will be differently most likely.
Sample Sheet List:
public List<string> GetTestTableList()
{
try
{
List<string> testlist = new List<string>();
testlist.Add("Table_1");
testlist.Add("Table_2");
testlist.Add("Table_3");
return testlist;
}
catch (Exception ex)
{
return null;
}
}
Part 5:
Next we will import the data from excel into SQL. This is a function I made but you can modify this to meet your needs.
Function:
private bool Import_Data_Into_SQL(string filepath, string SheetName, string Database, string Schema)
{
try
{
// sql table should match your sheet name in excel
string sqltable = SheetName;
// select all data from sheet by name
string exceldataquery = "select * from [" + SheetName + "$]";
//create our connection strings - Excel 2007 - This may differ based on Excel spreadsheet used
string excelconnectionstring = #"Provider=Microsoft.ACE.OLEDB.12.0; Data Source='" + filepath + " '; Extended Properties=Excel 8.0;";
string sqlconnectionstring = Properties.Settings.Default.SQL_Connection;
//series of commands to bulk copy data from the excel file into our sql table
OleDbConnection oledbconn = new OleDbConnection(excelconnectionstring);
OleDbCommand oledbcmd = new OleDbCommand(exceldataquery, oledbconn);
oledbconn.Open();
OleDbDataReader dr = oledbcmd.ExecuteReader();
SqlBulkCopy bulkcopy = new SqlBulkCopy(sqlconnectionstring);
bulkcopy.DestinationTableName = Database + "." + Schema +"." + sqltable;
while (dr.Read())
{
bulkcopy.WriteToServer(dr);
}
dr.Close();
oledbconn.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
I hope this helps. This was my workaround solution. Originally I wanted/tried to import my data using the .bak file but as pointed out above you can only do that if the sql server is local. So I hope this work around helps those who where faced with a similar issue as me. I'm not marking this as the answer because the above post answers the question but I'm posting this in case someone else needs this workaround. Thanks
Restore file must be on server. For installation use SQL script. This can be generated by SQL Server Management Studio (including data).
Right click on database. Choose "Tasks" - "Generate scripts". On second page of wizard choose "Advanced" and find "Types of data to script". Select "Schema and data" and save script to file.
Then use this code to run script on database
string scriptText = File.ReadAllText(scriptFile, Encoding.Default);
ExecuteBatch executeBatch = new ExecuteBatch();
StringCollection commandTexts = executeBatch.GetStatements(scriptText);
using (SqlConnection sqlConnection = new SqlConnection(conn))
{
sqlConnection.InfoMessage += SqlConnection_InfoMessage;
sqlConnection.Open();
for (int i = 0; i < commandTexts.Count; i++)
{
try
{
log.InfoFormat("Executing statement {0}", i + 1);
string commandText = commandTexts[i];
using (SqlCommand sqlCommand = sqlConnection.CreateCommand())
{
log.Debug(commandText);
sqlCommand.CommandText = commandText;
sqlCommand.CommandTimeout = 300;
int r = sqlCommand.ExecuteNonQuery();
log.DebugFormat("{0} rows affected", r);
}
}
catch (Exception ex)
{
log.Warn("Executing command failed", ex);
try
{
sqlConnection.Open();
}
catch (Exception ex2)
{
log.Error("Cannot reopen connection", ex2);
}
}
}
sqlConnection.Close();
}

Backing up database with SMO

I have created a class to backup a database, the code is:
public bool BackupDatabase(string databasename)
{
bool success = false;
try
{
Backup dbBackup = new Backup();
string SqlInstance = #"SERVER\INSTANCE";
string User = ExtractPureUsername(System.Security.Principal.WindowsIdentity.GetCurrent().Name);
string BackupLocation = #"\\SERVER\FOLDER\BACKUPTEST_" + User.ToString() + ".bak";
Server srv = new Server(SqlInstance);
dbBackup.Action = BackupActionType.Database;
dbBackup.Database = databasename;
dbBackup.Devices.AddDevice(BackupLocation, DeviceType.File);
dbBackup.BackupSetName = "Test database backup";
dbBackup.ExpirationDate = DateTime.Today.AddDays(10);
dbBackup.Initialize = false;
dbBackup.PercentComplete += CompletionStatusInPercent;
dbBackup.Complete += Backup_Completed;
dbBackup.SqlBackup(srv);
success = true;
}
catch(Exception ex)
{
Console.WriteLine(ex);
Console.ReadKey();
}
return success;
}
This runs through, and I get the error:
Cannot open backup device - Operating System error 5
If I were to run the backup on the instance referenced, in SSMS, I can backup to the Bakup location that is specified. So I am presuming that the error is occuring becuase the backup is being initiated as the user that is running the C# program, and SQL server is having none of it. Is there a way to specify which user to run the backup as?
You probably have to set the correct file name as local path on the SQL Server machine. So for instance instead of \\ServerName\Whatever, use c:\Whatever. Make sure the file name you generate doesn't contain illegal characters, like \ or something.

Restore database using c# and smo

I am trying to restore a sql server .bak into an empty database using the following c# code:
string dbBakFile = GetBackFileFromZip(restoreConfig.TmpUnZipFolder,restoreConfig.DatabaseFileToRestore);
if (string.IsNullOrEmpty(dbBakFile))
{
response.Status = DatabaseResponseStatus.Error;
response.Message = "No .bak file found in " + restoreConfig.DatabaseToRestore;
return response;
}
var builder =
new SqlConnectionStringBuilder(
ConfigurationManager.ConnectionStrings["myserver"].ConnectionString);
var smoServer =
new Server(new ServerConnection(builder.DataSource,builder.UserID,builder.Password));
var db = smoServer.Databases[restoreConfig.DatabaseToRestore];
if (db != null)
{
smoServer.KillAllProcesses(restoreConfig.DatabaseToRestore);
log.Debug("all processes on db killed");
}
string dbPath = Path.Combine(db.PrimaryFilePath, restoreConfig.DatabaseToRestore + ".mdf");
log.Debug("db path is " +dbPath);
string logPath = Path.Combine(db.PrimaryFilePath,restoreConfig.DatabaseToRestore + "_Log.ldf");
log.Debug("log path is " + logPath);
var restore = new Restore();
var deviceItem =
new BackupDeviceItem(dbBakFile, DeviceType.File);
restore.DatabaseFiles.Add(dbPath);
restore.DatabaseFiles.Add(logPath);
restore.Devices.Add(deviceItem);
restore.Database = restoreConfig.DatabaseToRestore;
restore.FileNumber = 1;
restore.Action = RestoreActionType.Files;
restore.ReplaceDatabase = true;
restore.PercentCompleteNotification = 10;
restore.PercentComplete +=restore_PercentComplete;
restore.Complete += restore_Complete;
restore.SqlRestore(smoServer);
db = smoServer.Databases[restoreConfig.DatabaseToRestore];
db.SetOnline();
smoServer.Refresh();
db.Refresh();
I get the following error:
Microsoft.SqlServer.Management.Smo.FailedOperationException: Restore failed for Server 'IM-M4500\SQLEXPRESS'. ---> Microsoft.SqlServer.Management.Smo.SmoException: System.Data.SqlClient.SqlError: The backup set holds a backup of a database other than the existing 'new-test-44444' database
Yes it does hold different backup and I want to overwrite and replace it also want to move mdf and log files to new files. Am I missing something here in the options of restore?
Many thanks
Ismail
Ok fixed the issue I need to give it the current db logical file name what i was actually doing was giving it the new db logical file name so
//get the logical file names
DataTable dtFileList = restore.ReadFileList(smoServer);
string dbLogicalName = dtFileList.Rows[0][0].ToString();
string logLogicalName = dtFileList.Rows[1][0].ToString();
restore.RelocateFiles.Add(GetRelocateFile(dbLogicalName, dbPath));
restore.RelocateFiles.Add(GetRelocateFile(logLogicalName, logPath));
This works nicely.
Don't create a new database and try to restore on it. Instead use the below query.
RESTORE DATABASE dbname from disk='location' WITH MOVE 'data' TO 'name.mdf' MOVE '_Log' TO 'name_log.ldf'
To Replace the existing database, put its name on the dbname and use WITH REPLACE on the query

Copying table from one SQL Server to another

I'm trying to create a copy of a table (no data, just the schema) using SQL Server Management Objects (SMO), Transfer class. The only thing I haven't figured out is how to specify what server to copy to, when the servers are on different hosts. In my case, I want to copy from 10.1.2.x to 10.1.2.y. Is there any way to specify this, or does this class not support it?
Perhaps there are better C# solutions?
static void CreateTableFromTable(string fromConnection, string toConnection, string dbName, string tablename, bool copyData = false)
{
Server fromServer = new Server(new ServerConnection(new SqlConnection(fromConnection)));
Database db = fromServer.Databases[dbName];
Transfer transfer = new Transfer(db);
transfer.CopyAllObjects = false;
transfer.DropDestinationObjectsFirst = false;
transfer.CopySchema = false; //Database schema? Or Table schema? I DO NOT want to overwrite the db schema
transfer.CopyData = copyData;
transfer.DestinationServer = "?";
transfer.DestinationDatabase = dbName;
transfer.Options.IncludeIfNotExists = true;
transfer.ObjectList.Add(db.Tables[tablename]);
transfer.TransferData();
}
Have you tried import and export data wizard even importing with table data ,command line or GUI in SQL server 2005/8 and Mysql /MysqlWorkbench.
I'm not sure if you found another solution - or got this one working. If you did not the SMO Scripter object might be worth a look.
This MSDN example could be helpful. You could script the tables and dependencies you want and then open a connection to the destination database and execute the scripts.
static void Main(string[] args)
{
Server sourceServer = new Server("server");
String dbName = "database";
// Connect to the local, default instance of SQL Server.
// Reference the database.
Database db = sourceServer.Databases[dbName];
// Define a Scripter object and set the required scripting options.
Scripter scripter = new Scripter(sourceServer);
scripter.Options.ScriptDrops = false;
scripter.Options.WithDependencies = true;
scripter.Options.Indexes = true; // To include indexes
scripter.Options.DriAllConstraints = true; // to include referential constraints in the script
// Iterate through the tables in database and script each one. Display the script.
foreach (Table tb in db.Tables)
{
// check if the table is not a system table
if (tb.IsSystemObject == false)
{
Console.WriteLine("-- Scripting for table " + tb.Name);
// Generating script for table tb
System.Collections.Specialized.StringCollection sc = scripter.Script(new Urn[] { tb.Urn });
foreach (string st in sc)
{
//ado.net to destination
Console.WriteLine(st);//SqlCommand.ExecuteNonQuery();
}
Console.WriteLine("--");
}
}
}
Did you try to use SELECT ... INTO statement?
For example:
SELECT * INTO DestDatabase.TableName FROM SourceDatabase.TableName
If you don't want to copy data, just add a condition which will be return nothing, ex: WHERE Id = 0

Categories