Restoring SQL Server 2005 database using C#. Rollback issue - c#

Hi I'm just starting to learn C#. I am trying to restore a .bak file. However I am getting the error. Exclusive access cannot be obtained because the database is in use.
I did my research here and here both says I have to perform a rollback. I do not know how to apply rollback in my restore code.
public void RestoreDatabase(String RestorePath)
{
try
{
SqlConnection sqlCon = new SqlConnection("Data Source=RITZEL-PC\\SQLEXPRESS;User ID=NNIT-Admin;Password=password;Initial Catalog=master;");
ServerConnection connection = new ServerConnection(sqlCon);
Server sqlServer = new Server(connection);
Restore restoreDB = new Restore();
restoreDB.Database = "NNIT DB";
restoreDB.Action = RestoreActionType.Database;
restoreDB.Devices.AddDevice(RestorePath, DeviceType.File);
restoreDB.ReplaceDatabase = true; // will overwrite any existing DB
restoreDB.NoRecovery = false; // NoRecovery = true;
restoreDB.SqlRestore(sqlServer);
MessageBox.Show("Restored");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + " " + ex.InnerException);
}
}

Using SMO you can set user access and rollback like this:
Server sqlServer = new Server(connection);
Database db = sqlServer.Databases["DbToRestore"];
if (db != null)
{
sqlServer.KillAllProcesses(db.Name);
db.DatabaseOptions.UserAccess = DatabaseUserAccess.Multiple;
db.Alter(TerminationClause.RollbackTransactionsImmediately);
}
Restore restoreDB = new Restore();

Does this work?
SqlCommand cmd = new SqlCommand("ALTER DATABASE yourdatabasename SET MULTI_USER WITH ROLLBACK IMMEDIATE", sqlConn);
cmd.ExecuteNonQuery();

Related

AccessViolationException thrown by OleDbConnection.Open()

I am getting a System.AccessViolationException error when trying to use the .Open() method on my OleDbConnection variable. But, confoundingly, it doesn't seem to happen when I run my code on a different computer.
My code is for a service that I want running on a server. It appears to work correctly when I run it on my personal computer, but it throws the AccessViolationException error when I try running it on the server.
Code Versions:
I am writing in C# using Visual Studios 2019 on Windows 10
OleDbConnection is from "Assembly System.Data, Version=4.0.0.0"
ADOX is from "Assembly Interop.ADOX, Version=2.8.0.0", System.Runtime.InteropServices
ADODB is from "Assembly Interop.ADODB, Version=2.8.0.0", System.Runtime.InteropServices
My code:
internal static bool CreateMDB(MySchemaClass schema, string filePath)
{
OleDbConnection conn = null;
bool status = true;
string connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0}", filePath);
try
{
// Setup new file
catalog.Create(connectionString);
((ADODB.Connection)catalog.ActiveConnection).Close();
conn = new OleDbConnection(connectionString);
conn.Open(); // <-- Error occurs here
// Write to new file
WriteDataToFile(schema, conn);
}
catch (Exception ex)
{
status = false;
}
finally
{
if (conn != null)
conn.Close();
}
return status;
}
StackTrace:
The best lead I have found so far is this post, but I'm a bit hazy about how to proceed from that starting point.
To programmatically create an Access database do the following:
Add reference to Microsoft ADO Ext. 6.0 for DDL and Security
In VS menu, click Project
Select Add Reference
Click COM
Check Microsoft ADO Ext. 6.0 for DDL and Security
CreateAccessDatabase
public static string CreateAccessDatabase(string fullyQualifiedAccessFilename, string dbPassword = "")
{
string connectionString = String.Format(#"Provider = Microsoft.ACE.OLEDB.12.0; Data Source = {0}", fullyQualifiedAccessFilename);
if (String.IsNullOrEmpty(fullyQualifiedAccessFilename))
{
throw new Exception("Error (CreateAccessDatabase) - Database filename is null or empty.");
}
if (!String.IsNullOrEmpty(dbPassword))
{
connectionString = String.Format(#"Provider = Microsoft.ACE.OLEDB.12.0; Data Source = {0};Jet OLEDB:Database Password='{1}'", fullyQualifiedAccessFilename, dbPassword);
}
//create new instance
ADOX.Catalog cat = new ADOX.Catalog();
//create Access database
cat.Create(connectionString);
//close connection
cat.ActiveConnection.Close();
//release COM object
System.Runtime.InteropServices.Marshal.ReleaseComObject(cat);
GC.Collect();
cat = null;
return String.Format("Database created '{0}'", fullyQualifiedAccessFilename);
}
Usage:
string result = string.Empty;
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Access Database 2000-2003 (*.mdb)|*.mdb|Access Database 2007 (*.accdb)|*.accdb";
if (sfd.ShowDialog() == DialogResult.OK)
{
//create Access database
result = CreateAccessDatabase(sfd.FileName);
}
To programmatically create a table in an Access database:
Add using statement:
using System.Data.OleDb;
CreateTableProduct:
public static int CreateTableProduct(string fullyQualifiedAccessFilename, string dbPassword = "")
{
int result = 0;
string connectionString = String.Format(#"Provider = Microsoft.ACE.OLEDB.12.0; Data Source = {0}", fullyQualifiedAccessFilename);
if (!String.IsNullOrEmpty(dbPassword))
{
connectionString = String.Format(#"Provider = Microsoft.ACE.OLEDB.12.0; Data Source = {0};Jet OLEDB:Database Password='{1}'", fullyQualifiedAccessFilename, dbPassword);
}
string sqlText = string.Empty;
sqlText = "CREATE TABLE Product ";
sqlText += "(ID AUTOINCREMENT not null primary key,";
sqlText += " Name varchar(50) not null,";
sqlText += " Price currency, Quantity integer);";
using (OleDbConnection con = new OleDbConnection(connectionString))
{
//open connection
con.Open();
using (OleDbCommand sqlCmd = new OleDbCommand(sqlText, con))
{
//execute command
result = sqlCmd.ExecuteNonQuery();
}
}
return result;
}
Resources:
ADO Features for each Release
Which Access file format should I use?
CREATE TABLE statement (Microsoft Access SQL)
System.Data.OleDb Namespace
The underwhelming answer to the problem is that my server was lacking some of the files my personal computer had, and only needed the correct files installed.
The missing piece in this case was the Microsoft Access Database Engine 2016 Redistributable, which I ended up finding here. Running that executable on my server got the needed files and everything worked after that.

How to backup SQL localDB with SMO

I am trying to backup a SQL localDB database using SMO but with no success.
What is wrong with my code?
progressBar.Value = 0;
SaveFileDialog sfd = new SaveFileDialog();
string stringCon = #"Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\whdb.mdf;Integrated Security=True";
sfd.Filter = "Database backup files (*.bak)|*.bak";
sfd.Title = "Create Database Backup";
sfd.FileName = DateTime.Today.ToString("ddMMMyyyy") + ".bak";
if (sfd.ShowDialog() == DialogResult.OK)
{
using (SqlConnection conn = new SqlConnection(stringCon))
{
ServerConnection servConn = new ServerConnection(conn);
SqlConnection.ClearAllPools();
conn.Open();
servConn.Connect();
try {
Server serverdb = new Server(servConn);
Backup backupdb = new Backup() { Action = BackupActionType.Database, Database="whdb"};
backupdb.Devices.AddDevice(sfd.FileName, DeviceType.File);
backupdb.Initialize = true;
backupdb.Incremental = false;
backupdb.SqlBackupAsync(serverdb);
progressBar.Value = 100;
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
This the exception I get when I run it:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Data.dll
Additional information: ExecuteNonQuery requires an open and available Connection. The connection's current state is closed.
I think the problem is that you are using
backupdb.SqlBackupAsync(serverdb);
which starts the backup running in the background. But, by the time the backup gets around to running, you have closed your connection.
You could either use
backupdb.SqlBackup(serverdb);
or else remove your using and close and find some was to manually close the connection after the backup.

Reason for file lock while bulkcopy its data to table

I'm browsing a .MDF file and uploading its data to a SQL Server database table.
My app has a browse button to select the file and then upload button to perform the bulkcopy operation.
But in second attempt when I choose the .mdf file using OpenFileDialog, it throws an error saying the file is already being used.
private void labelBrowse_Click(object sender, System.EventArgs e)
{
try
{
System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog();
openFileDialog.Title = "Select the MDF file to upload";
openFileDialog.Filter = "Data|*.mdf";
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
textBoxMdfFilePath.Text = openFileDialog.FileName.ToString();
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString());
}
}
private void labelUpload_Click(object sender, System.EventArgs e)
{
if (string.IsNullOrEmpty(textBoxMdfFilePath.Text.Trim()))
{
System.Windows.Forms.MessageBox.Show("Please select a MDF file to upload");
textBoxMdfFilePath.Text = "";
return;
}
else if (!(System.IO.Path.GetFileName(textBoxMdfFilePath.Text).Equals("Audit.mdf", System.StringComparison.InvariantCultureIgnoreCase)) && !(System.IO.Path.GetFileName(textBoxMdfFilePath.Text).Equals("IPAUDIT.mdf", System.StringComparison.InvariantCultureIgnoreCase)))
{
System.Windows.Forms.MessageBox.Show("Please select the correct MDF file to upload");
textBoxMdfFilePath.Text = "";
return;
}
else
{
uploadToServer(textBoxMdfFilePath.Text);
}
}
void uploadToServer(string path)
{
try
{
string mdfConnectionString = #"data source=172.16.2.136;attachdbfilename=" + path + ";" + "integrated security=true;" + "connect timeout=30;" + "user instance=true";
System.Data.SqlClient.SqlConnection sqlconnection = new System.Data.SqlClient.SqlConnection(mdfConnectionString);
if (System.IO.Path.GetFileName(path).Equals("Audit.mdf", System.StringComparison.InvariantCultureIgnoreCase))
{
System.Data.SqlClient.SqlCommand sqlcommand = new System.Data.SqlClient.SqlCommand("select * from [D-Audit]", sqlconnection);
sqlconnection.Open();
System.Data.DataTable dataTable = new System.Data.DataTable();
dataTable.Load(sqlcommand.ExecuteReader());
sqlconnection.Close();
System.Data.SqlClient.SqlBulkCopy sqlbulkcopy = new System.Data.SqlClient.SqlBulkCopy(System.Configuration.ConfigurationManager.ConnectionStrings["myServerAudit"].ConnectionString);
sqlbulkcopy.DestinationTableName = "[dbo].[D-Audit]";
sqlbulkcopy.BulkCopyTimeout = 1800;
sqlbulkcopy.WriteToServer(dataTable);
sqlbulkcopy.Close();
System.Windows.Forms.MessageBox.Show("Data Uploaded Successfully");
textBoxMdfFilePath.Text = "";
}
else if (System.IO.Path.GetFileName(path).Equals("IPAudit.mdf", System.StringComparison.InvariantCultureIgnoreCase))
{
System.Data.SqlClient.SqlCommand sqlcommand = new System.Data.SqlClient.SqlCommand("select * from IpTransaction", sqlconnection);
sqlconnection.Open();
System.Data.DataTable dataTable = new System.Data.DataTable();
dataTable.Load(sqlcommand.ExecuteReader());
sqlconnection.Close();
System.Data.SqlClient.SqlBulkCopy sqlbulkcopy = new System.Data.SqlClient.SqlBulkCopy(System.Configuration.ConfigurationManager.ConnectionStrings["myServerIPAudit"].ConnectionString);
sqlbulkcopy.DestinationTableName = "IpTransaction";
sqlbulkcopy.BulkCopyTimeout = 1800;
sqlbulkcopy.WriteToServer(dataTable);
sqlbulkcopy.Close();
System.Windows.Forms.MessageBox.Show("Data Uploaded Successfully");
textBoxMdfFilePath.Text = "";
}
sqlconnection.Close();
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
You need to set Pooling=False in your connection string
Whats happening here is SQLBulkcopy is creating its own SQLConnection object with connection string you provided. By default, the Connection Pool is true...so when sqlconnection object is created by SQLBulkCopy, it is added in connection pool and connection persists even after application is closed.This mechanism is to optimize performance but in your case that's what is causing issue...so adding Pooling=false in your connection should solve your problem
ConnectionString = "Data Source=MYSQL;Initial Catalog=MyDatabase;Integrated Security=true;Pooling=false"
Additinal Info
It is good practice to wrap your SQLBulkCopy with Using block as it is of type IDisposable so you need not call close explicitly.
When you say on "second attempt", I'm assuming it works on first click but not on subsequent clicks. If that's the case, then the connection is probably not being released and still holding on to the DB. You may try disposing all IDisposables in your code before leaving the event handler.

bulkCopy.WriteToServer does not copy

I'm trying to use bulkCopy.WriteToServer() to copy data from my remote SQL Server Express to my local SQL Server Express, but no data is ever written to my local SQL Server Express database.
The code jumps over the WriteToServer() method instantly... I have no idea if it's failing on the inside and not showing an error message though
I have read How to duplicate a SQL Server 2000 table programatically using .NET 2.0? and I am using very similar code. Although I am using SQL Server 2008 Express on remote and a local to SQL Server 2014 Express:
using (SqlConnection remoteConnection = new SqlConnection(remoteConnectionString))
{
var query = "SELECT * FROM information_schema.tables WHERE table_type = 'base table'";
SqlCommand commandGetTables = new SqlCommand(query, remoteConnection);
try
{
remoteConnection.Open();
SqlDataReader results = commandGetTables.ExecuteReader();
while (results.Read())
{
tables.Add(results.GetString(2));
}
results.Close();
}
catch (Exception ex)
{
//stuff
}
finally
{
remoteConnection.Close();
}
remoteConnection.Open();
foreach (var table in tables)
{
// Get data from the source table as a SqlDataReader.
var commandSourceData = new SqlCommand("SELECT * FROM " + table + ";", remoteConnection);
var reader = commandSourceData.ExecuteReader();
using (SqlConnection destinationConnection = new SqlConnection(destinationConnectionString))
{
destinationConnection.Open();
using (var bulkCopy = new SqlBulkCopy(destinationConnection))
{
bulkCopy.DestinationTableName = table;
try
{
// Write from the source to the destination.
bulkCopy.WriteToServer(reader);
}
catch (Exception ex)
{
//stuff
}
finally
{
//stuff removed for this post
}
}
}
}
remoteConnection.Close();
}
return true;
I know this could be subject to SQL injection etc, but this app is only used by me and not the issue here.
What am I doing wrong?
Edit
I checked the value of reader (var reader = commandSourceData.ExecuteReader();) and it has entries as I would expect, meaning he reading from the remote is fine.
bulkCopy.DestinationTableName = table;
bulkCopy.WriteToServer(reader);
these lines are wrong it's supposed to look like this..
bulkCopy.DestinationTableName = "dbo." + DataTable.TableName;
bulkCopy.WriteToServer(DataTable);

How to backup - restore .mdf database

I have developed a windows application with vs2010 and c#. I would like to know a way to backup and restore my local mdf database programmatically. With sdf database I use File Copy but it doesn't seem to work with mdf files.
Can anyone help?
Try in this way:
Go to Sql Management Studio and select the database you want to
backup
Right click and select 'Tasks' -> 'Backup'
Adjust the parameters as you like, but don't confirm the dialog
Press the button SCRIPT and dismiss the dialog
On the query window insert the following text before the backup
command
CREATE PROCEDURE DO_BACKUP
AS
BEGIN
-- HERE GOES THE BACKUP TEXT CREATED BY THE SCRIPT BUTTON
-- FOR EXAMPLE
BACKUP DATABASE [Customers]
TO DISK = N'E:\backups\customers.bak'
WITH NOFORMAT, NOINIT,
NAME = N'Customers - Full Database Backup',
SKIP, NOREWIND, NOUNLOAD, STATS = 10
END
and execute (selecting the correct database) using the exclamation mark button.
Now you have a stored procedure called DO_BACKUP that you can call from your code using the normal ADO.NET objects like SqlConnection and SqlCommand
I struggled a lot with this, and the accepted answer does not do the trick, so here is a solution that worked for me (thanks to dnxit)
it may help someone.
Backup
try
{
var dlg = new System.Windows.Forms.FolderBrowserDialog();
var result = dlg.ShowDialog(this.GetIWin32Window());
if (result.ToString() == "OK")
{
var dbfileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "LibraryManger.mdf");
var backupConn = new SqlConnection { ConnectionString = eb.GetConnectionString() };
backupConn.Open();
var backupcomm = backupConn.CreateCommand();
var backupdb = $#"BACKUP DATABASE ""{dbfileName}"" TO DISK='{Path.Combine(dlg.SelectedPath,"LibraryManagement.bak")}'";
var backupcreatecomm = new SqlCommand(backupdb, backupConn);
backupcreatecomm.ExecuteNonQuery();
backupConn.Close();
MessageBox.Show($"Database backup has successfully stored in {Path.Combine(dlg.SelectedPath, "LibraryManagement.bak")}", "Confirmation");
}
}
catch (Exception ex)
{
if(ex.Message.Contains("Operating system error"))
{
MessageBox.Show("Please chose a public folder.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
Restore
You'll have to close existing connection before you restore
try
{
if (eb != null)
{
eb.DisposeConnection();
eb = null;
}
var dlg = new OpenFileDialog();
dlg.InitialDirectory = "C:\\";
dlg.Filter = "Database file (*.bak)|*.bak";
dlg.RestoreDirectory = true;
if (Equals(dlg.ShowDialog(), true))
{
using (var con = new SqlConnection())
{
con.ConnectionString = #"Data Source=(LocalDB)\MSSQLLocalDB;Database=Master;Integrated Security=True;Connect Timeout=30;";
con.Open();
var dbfileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "LibraryManger.mdf");
using (var cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = $#"RESTORE DATABASE ""{dbfileName}"" FROM DISK='{dlg.FileName}'";
cmd.ExecuteNonQuery();
}
con.Close();
}
MessageBox.Show($"Database backup has successfully restored.", "Confirmation");
eb = new EntityBroker.EntityBroker();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}

Categories