0

I want to restore a SQL Server localdb database. I tried with this code:

Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    Dim ofd As New OpenFileDialog() With
        {
            .Filter = "Backup file | *.bak",
            .FileName = ""
        }

    If ofd.ShowDialog = System.Windows.Forms.DialogResult.OK Then
            Cursor = Cursors.WaitCursor
            Dim csb = New SqlConnectionStringBuilder("Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\Users\CHAKER\Documents\RestoDB.mdf;Integrated Security=True;Connect Timeout=30")
            Dim dbFullPath = csb.AttachDBFilename

            Dim sql = String.Format("RESTORE DATABASE ""{0}"" From DISK = '{1}'", dbFullPath, ofd.FileName)
            Dim cmd As New SqlCommand(sql, con)
            cmd.ExecuteNonQuery()
            MsgBox("Restore complete")
            Cursor = Cursors.Default

        End If

    End Sub

I get this exception :

RESTORE cannot process database 'C:\Users\CHAKER\Documents\RestoDB.mdf' because it is in use by this session. It is recommended that the master database be used when performing this operation.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Chaker
  • 59
  • 6
  • Have you tried adding "Initial Catalog=master" to your connection string? – Dan Guzman Nov 27 '21 at 12:16
  • @DanGuzman I tried with "Initial Catalog=master" i get the same exception – Chaker Nov 27 '21 at 13:31
  • 2
    I don't see where you are declaring and opening the connection. Try `con.ChangeDatabase("master")` before executing the restore. – Dan Guzman Nov 27 '21 at 13:46
  • Everywhere in the code that opens a database connection has to close it (and call `.Dispose()` on the connection) when it has finished using it. You should not have a global con variable. You can see the pattern in [this answer](https://stackoverflow.com/a/60210748/1115360) by me, although it uses OleDbConnection instead of SqlConnection, etc. – Andrew Morton Nov 27 '21 at 16:47
  • @AndrewMorton i try con.ChangeDatabase("master") i get this exception Additional information: To restore the master database, the server must be running in single-user mode. For information on starting in single-user mode, see "How to: Start an Instance of SQL Server (sqlservr.exe)" in Books Online. – Chaker Nov 28 '21 at 11:48
  • @Chaker In that case, [Restore database in SQL LocalDB using VB.NET](https://stackoverflow.com/q/33051436/1115360) looks like it will be of use to you. – Andrew Morton Nov 28 '21 at 11:56

1 Answers1

0

You have a number of issues with your code:

  • Your primary issue: you are restoring a database by filename, but you have already attached it using AttachDbFileName, and such a database cannot be restored over. Using AttachDbFileName is in any case deprecated. You should instead attach it to LocalDB using the normal CREATE DATABASE FOR ATTACH syntax.
  • Preferably you should not hard-code your connection string, store it in a settings file instead.
  • You need to dispose your connection and command objects with Using. Do not cache the connection object.
  • Do not block the thread with a MessageBox while the connection is open
  • You should use parameterization on the RESTORE command, rather than injecting the names directly
In settings file:
ConnectionString "Data Source=(LocalDB)\v11.0;Initial Catalog=RestoDB;Integrated Security=True;Connect Timeout=30"
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    Dim ofd As New OpenFileDialog() With
        {
            .Filter = "Backup file | *.bak",
            .FileName = ""
        }

    If ofd.ShowDialog <> System.Windows.Forms.DialogResult.OK Then Exit Sub

    Try
        Cursor = Cursors.WaitCursor
        Const sql = "RESTORE DATABASE @DB From DISK = @FileName"
        Using con As New SqlConnection(Properties.ConnectionString),
              cmd As New SqlCommand(sql, con)
            cmd.Parameters.Add("@DB", SqlDbType.NVarChar, 128).Value = con.Database
            cmd.Parameters.Add("@FileName", SqlDbType.NVarChar, 255).Value = ofd.FileName
            cmd.ExecuteNonQuery()
        End Using
        MsgBox("Restore complete")
    Catch ex As Exception
        MsgBox("Error: " + ex.Message)
    Finally
        Cursor = Cursors.Default
    End Finally

End Sub
Charlieface
  • 52,284
  • 6
  • 19
  • 43
  • the code not working – Chaker Nov 28 '21 at 11:46
  • Are you sure that SQL parameters can be used there? In the same way that column names can't be SQL parameters. Also, the syntax for the [Try...Catch](https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/try-catch-finally-statement) is incorrect. – Andrew Morton Nov 28 '21 at 11:51
  • @Chaker Should be working now. Unless you are getting some exception, if so what is it? – Charlieface Nov 28 '21 at 12:17
  • 1
    @AndrewMorton Thanks, haven't written VB for a while. Syntax for `RESTORE` is `RESTORE DATABASE { database_name | @database_name_var } [ FROM [ ,...n ] ]` and ` ::= {{ logical_backup_device_name | @logical_backup_device_name_var } | { DISK | TAPE | URL } = { 'physical_backup_device_name' | @physical_backup_device_name_var }}` so it can be parameterized, see https://learn.microsoft.com/en-us/sql/t-sql/statements/restore-statements-transact-sql?view=sql-server-ver15#syntax – Charlieface Nov 28 '21 at 12:20
  • @AndrewMorton Eg this is valid code, see https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=b2200b5e9219708a52c73ada42dacd9b – Charlieface Nov 28 '21 at 13:05
  • @Charlieface It's OK, I believe you :) The problem might be as written in the OP's comment under the question, regarding needing to be in single-user mode. The Q&A I replied with looks to set the mode appropriately. – Andrew Morton Nov 28 '21 at 13:13