I want to restore a SQL Server database (.bak
) using .NET Core. Here is my empty website on GitHub so you can see the current config.
Restoring a database is fairly simple in the full .NET Framework - as can bee seen here.
Is there a way to do it from .NET Core directly, or will I need to reference the .NET Framework and use a .NET Framework class library?
No matter how I try, I can't get it to work.
EDIT
I tried adding SQLManagementObject, but can't. I'm on .NET Core 2.0.
EDIT 2
Our old projects are largely ADO.NET. They use (extensively) the following DLL's that I cannot bring into my .NET Core Project:
UPDATE: With .Net Core 2.0 you can use Microsoft.SqlServer.SqlManagementObjects (140.17265.0). SQL Server Management Objects (SMO) Framework You can use SQL SMO under Windows and Linux.
Microsoft.SqlServer.SqlManagementObjects depends on System.Data.SqlClient (4.5.0)
Simple SMO backup example:
ServerConnection serverConnection = new ServerConnection("192.168.1.1", "user", "password");
Server server = new Server(serverConnection);
Database database = server.Databases["AdventureWorks"];
Backup backup = new Backup();
backup.Action = BackupActionType.Database;
backup.BackupSetDescription = "AdventureWorks - full backup";
backup.BackupSetName = "AdventureWorks backup";
backup.Database = "AdventureWorks";
BackupDeviceItem deviceItem = new BackupDeviceItem("AdventureWorks_Full_Backup.bak", DeviceType.File);
backup.Devices.Add(deviceItem);
backup.Incremental = false;
backup.LogTruncation = BackupTruncateLogType.Truncate;
backup.SqlBackup(server);
In .NetCore to backup/restore SQL Server database you can use common ADO.NET SqlConnection and SqlCommand objects. To customize backup/restore you need know the syntax of T-SQL BACKUP/RESTORE statements. Please consult with
RESTORE Statements (T-SQL)
BACKUP Statements (T-SQL)
using System;
using System.Data;
using System.Data.SqlClient;
namespace BackupRestore
{
class Program
{
static void Main(string[] args)
{
BackupDatabase("test", @"C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\test.bak");
RestoreDatabase("test", @"C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\test.bak");
}
private static void RestoreDatabase(string databaseName, string backupPath)
{
string commandText = $@"USE [master];
ALTER DATABASE [{databaseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE [{databaseName}] FROM DISK = N'{backupPath}' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 5;
ALTER DATABASE [{databaseName}] SET MULTI_USER;";
SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder
{
DataSource = "localhost",
InitialCatalog = "master",
IntegratedSecurity = true
};
using (SqlConnection connection = new SqlConnection(connectionStringBuilder.ConnectionString))
{
connection.Open();
connection.InfoMessage += Connection_InfoMessage;
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = commandText;
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
}
}
}
private static void BackupDatabase(string databaseName, string backupPath)
{
string commandText = $@"BACKUP DATABASE [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, INIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder
{
DataSource = "localhost",
InitialCatalog = "master",
IntegratedSecurity = true
};
using (SqlConnection connection = new SqlConnection(connectionStringBuilder.ConnectionString))
{
connection.Open();
connection.InfoMessage += Connection_InfoMessage;
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = commandText;
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
}
}
}
private static void Connection_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
Console.WriteLine(e.Message);
}
}
}
To RESTORE a database with new name for example newtest, you need execute next statement
RESTORE DATABASE [newtest]
FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\test.bak' WITH FILE = 1,
MOVE N'test' TO N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\newtest.mdf',
MOVE N'test_log' TO N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\newtest_log.ldf', NOUNLOAD, STATS = 5
You should not use SMO, SMO is created for those that would like to manage SQL server in a "manage SQL server type application", you're bound to run into versioning issues that you would not have when using simple TSQL commands. TSQL commands work quite well on .net Core.
One thing I'd like to mention is that you're opening your application to some troubling permissions.
if you need to restore your database you perhaps pass this over to another task set up for this. you can then have this "pooling task" deal with this.
Let assume you accept my concerns mentioned above (not a compleat list) and you would entertain the idea that you would like to offload this I would suggest you use a Task in SQL server for this. You can schedule these tasks to run ever ... second/ minute/ day even loop them.
You can have "N" steps in a task, 1 Step you use to "test for the restore condition" he following steps you use to do the restore in a managed way, when you're done you update a log/ send a mail etc.
When using a task you can run it using a user with the appropriate rights and when setting the database in single user mode your website will lose its connection and will be forced to wait for the restore job to restore the database state.
For you to be able to use SQL jobs you will need to make sure that this is activated on the server.
As per the content of your task, the TSQL statement if you would like to could look something like this, please note you need to validate that the backup file reported by SQL server actually exists, this used master.sys.xp_cmdshell for that:
USE Master;
GO
SET NOCOUNT ON
-- 1 - Variable declaration
DECLARE @dbName sysname
DECLARE @backupPath NVARCHAR(500)
DECLARE @cmd NVARCHAR(500)
DECLARE @fileList TABLE (backupFile NVARCHAR(255))
DECLARE @lastFullBackup NVARCHAR(500)
DECLARE @lastDiffBackup NVARCHAR(500)
DECLARE @backupFile NVARCHAR(500)
-- 2 - Initialize variables
SET @dbName = 'Customer'
SET @backupPath = 'D:\SQLBackups\'
-- 3 - get list of files
SET @cmd = 'DIR /b "' + @backupPath + '"'
INSERT INTO @fileList(backupFile)
EXEC master.sys.xp_cmdshell @cmd
-- 4 - Find latest full backup
SELECT @lastFullBackup = MAX(backupFile)
FROM @fileList
WHERE backupFile LIKE '%.BAK'
AND backupFile LIKE @dbName + '%'
SET @cmd = 'RESTORE DATABASE [' + @dbName + '] FROM DISK = '''
+ @backupPath + @lastFullBackup + ''' WITH NORECOVERY, REPLACE'
PRINT @cmd
-- 4 - Find latest diff backup
SELECT @lastDiffBackup = MAX(backupFile)
FROM @fileList
WHERE backupFile LIKE '%.DIF'
AND backupFile LIKE @dbName + '%'
AND backupFile > @lastFullBackup
-- check to make sure there is a diff backup
IF @lastDiffBackup IS NOT NULL
BEGIN
SET @cmd = 'RESTORE DATABASE [' + @dbName + '] FROM DISK = '''
+ @backupPath + @lastDiffBackup + ''' WITH NORECOVERY'
PRINT @cmd
SET @lastFullBackup = @lastDiffBackup
END
-- 5 - check for log backups
DECLARE backupFiles CURSOR FOR
SELECT backupFile
FROM @fileList
WHERE backupFile LIKE '%.TRN'
AND backupFile LIKE @dbName + '%'
AND backupFile > @lastFullBackup
OPEN backupFiles
-- Loop through all the files for the database
FETCH NEXT FROM backupFiles INTO @backupFile
WHILE @@FETCH_STATUS = 0
BEGIN
SET @cmd = 'RESTORE LOG [' + @dbName + '] FROM DISK = '''
+ @backupPath + @backupFile + ''' WITH NORECOVERY'
PRINT @cmd
FETCH NEXT FROM backupFiles INTO @backupFile
END
CLOSE backupFiles
DEALLOCATE backupFiles
-- 6 - put database in a useable state
SET @cmd = 'RESTORE DATABASE [' + @dbName + '] WITH RECOVERY'
PRINT @cmd
A safer way to do this is to query the server and hope the location is valid:
SELECT
bs.database_name,
bs.backup_start_date,
bmf.physical_device_name
FROM
msdb.dbo.backupmediafamily bmf
JOIN
msdb.dbo.backupset bs ON bs.media_set_id = bmf.media_set_id
WHERE
bs.database_name = 'MyDB'
ORDER BY
bmf.media_set_id DESC;
both methods will fail if the source is on tape or hosted in an Amazon or Microsoft cloud.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With