I try to copy a database using smo, but I get the error:
"User, group, or role '%' already exists in the current database"
My code:
var conn = GetServerConnection();
var server = new Server(conn);
var sourceDb = server.Databases[sourceDatabase.Name];
var destinationDbName = GetNameForDatabase(dbName);
var destinationDb = new Database(server, destinationDbName);
destinationDb.Create();
var transfer = new Transfer(sourceDb) {
DestinationDatabase = destinationDbName,
DestinationServer = server.Name,
DestinationLoginSecure = true,
CopySchema = true,
CopyAllTables = true,
CopyData = true,
CopyAllUsers = false,
};
transfer.Options.WithDependencies = true;
transfer.Options.ContinueScriptingOnError = true;
transfer.TransferData();
Thanks in advance for any suggestions!
Do you have any mappings from the database server to the source database? Try removing those before attempting the copy.
I was copying the database using the Copy Database wizard in order to create a test database on the same server, and got this error. The problem was that the source database had a user login mapped to that database. Somewhere in the mix of it all, the Copy Database wizard was trying to add a user to the destination database via a straight copy, but also add the same user through the mapping. The trick was to remove the mapping of the source database, then copy the database, then add the mapping back to the source (it was already added at the destination).
Related
This question already has answers here:
Backup Permissions
(5 answers)
Closed 1 year ago.
I'm trying to back up my database using this C# code How to backup and restore SQL Server in WPF with C# and Entity Framework
private static void CreateBackup(string databaseName, string backupFilePath)
{
GlobalConfig gb = new GlobalConfig();
string connectionString = gb.GetConnectionString();
backupFilePath = backupFilePath + "\\" + databaseName + ".bak";
backupFilePath = #""+backupFilePath;
var backupCommand = "BACKUP DATABASE #databaseName TO DISK = #backupFilePath";
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(backupCommand, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#databaseName", databaseName);
cmd.Parameters.AddWithValue("#backupFilePath", backupFilePath);
cmd.ExecuteNonQuery();
}
}
CreateBackup("Test","C:\Desktop\Backup\\Test.bak");
But I got this error :
Cannot open backup device 'C:\Desktop\Backup\Test.bak'. Operating system error 5(Access is denied.).
What I'm doing wrong with this code?
How can I fix this error?
The SQL Server process typically does not run with the permissions of the currently logged in user, therefore it cannot access the users desktop (nor most of the folders of the user or any network folders). It is not possible to freely choose the folder for the backup.
Your best solution is to export to a folder where the server process has access to (i.e. the system temp folder) and then copy the backup from there to wherever you want it.
private static void CreateBackup(string databaseName, string backupFilePath)
{
GlobalConfig gb = new GlobalConfig();
string connectionString = gb.GetConnectionString();
// Create the backup in the temp directory (the server should have access there)
var backup = Path.Combine(Path.GetTempPath(), "TemporaryBackup.bak");
var backupCommand = "BACKUP DATABASE #databaseName TO DISK = #backup";
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(backupCommand, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#databaseName", databaseName);
cmd.Parameters.AddWithValue("#backup", backup);
cmd.ExecuteNonQuery();
}
File.Copy(backup, backupFilePath); // Copy file to final location
}
I have successfully backed up SQL Server databases using Microsoft.SqlServer.Management.Smo.Backup, might want to try that. Mine was in VB years ago but it is still working today. Here is the VB code if it helps:
Dim mySourceServer As New Server(My.Settings.SQLInstance)
Dim bkpDBFullWithCompression As New Backup()
' Specify whether you want to back up database or files or log
Me.Cursor = Cursors.WaitCursor()
bkpDBFullWithCompression.Action = BackupActionType.Database
' Specify the name of the database to back up
bkpDBFullWithCompression.Database = _sBackupDatabaseName
bkpDBFullWithCompression.CompressionOption = BackupCompressionOptions.[On]
bkpDBFullWithCompression.Devices.AddDevice(_sBackupFilePath, DeviceType.File)
bkpDBFullWithCompression.BackupSetName = _sBackupDatabaseName + " database Backup - Compressed"
bkpDBFullWithCompression.BackupSetDescription = _sBackupDatabaseName + " database - Full Backup"
Try
bkpDBFullWithCompression.SqlBackup(mySourceServer)
Catch ex As SmoException
blSuccess = False '
Me.Cursor = Cursors.Default
End Try
This solution ( Check Local System account instead of This account ) worked for me, but I didn't have any idea if it's a good solution for security or no.
You can find the LogOn tab setting under this :
Services -> SQL Server -> Properties -> Log on
Is there any way of changing a user permissions for table in Access database using C#? I have a problem to read and insert into table which has denied read/insert rights for current user. When I change manually permissions works, but I want to change it programatically by C#. I've been tried this SQL command:
GRANT SELECT ON TABLE myTable TO Admin
but it's seems that I don't have rights for that action. This how it looks in Access.
Edit:
To call #rybo103 method, I've used this code, but I'm getting an error.
DAO.DBEngine engine = new DAO.DBEngine();
string path = #"C:\Database.mdb";
DAO.Database db = engine.OpenDatabase(path, false, false, "");
List<DAO.PermissionEnum> flags = new List<PermissionEnum>();
flags.Add(DAO.PermissionEnum.dbSecFullAccess);
AddPermissions(db, "ECR", "Admins", flags);
Error:
Cannot open the Microsoft Office Access database engine workgroup information file.
In Access you will need to use DAO to make these sort of schema changes.
https://msdn.microsoft.com/en-us/library/office/ff835373.aspx
void AddPermissions(DAO.Database db, string tableName, string groupName, IList<DAO.PermissionEnum> addFlags)
{
var container = db.DaoDB.Containers["Tables"];
var document = container.Documents[tableName];
document.UserName = groupName;
int permissions = document.Permissions;
foreach (var flag in addFlags)
{
permissions = permissions | (int)flag;
}
document.Permissions = permissions;
}
#HansUp sugestons helped. Finnaly, I added Jet OLEDB:System Database property to my OleDb connection string and it works. Even it works without GRANT command.
I'm trying to set a SQL Server database owner using C# and SMO. If the database does not exist, I can assign its owner. But if I want to set the owner of an existing database, I get an error.
Working code for new database:
Server server = new Server("WINSERVER\\SQLEXPR");
server.ConnectionContext.LoginSecure = true;
Database database = new Database(server, "MyDatabase");
db.Create();
database.SetOwner("SOMEOWNER", true)
database.Refresh();
Not working code for an existing database:
Server server = new Server("WINSERVER\\SQLEXPR");
server.ConnectionContext.LoginSecure = true;
Database database = new Database(server, "MyDatabase");
database.SetOwner("SOMEOWNER", true)
database.Refresh();
Error:
Microsoft.SqlServer.Management.Smo.InvalidSmoOperationException: You cannot execute this operation since the object has not benn created.
Server server = new Server("WINSERVER\\SQLEXPR");
server.ConnectionContext.LoginSecure = true;
// changed line below
Database database = server.Databases["MyDatabase"];
database.SetOwner("SOMEOWNER", true)
database.Refresh();
No need to create a new Database object, just pull it from the database collection on the server already.
In SMO (C#), what is the equivalent to...
ALTER DATABASE db SET HADR OFF;
I've looked in the documentation for both the Database and AvailabilityDatabase classes and nothing is really jumping out at me.
The end goal here is to drop a database that is a member of an Availability Group. Currently, we are doing this by first turning HADR off and then dropping the database on all secondary servers, and then, on the primary server, removing the database from the Availability Group and dropping the database there.
Here is the process I've settled on for using SMO to drop a database that is joined to an AlwaysOn Availability Group...
Server primaryServer = new Server();
AvailabilityDatabase pDb = primaryServer.AvailabilityGroups[agName].AvailabilityDatabases[dbName];
pDb.SuspendDataMovement();
while (!pDb.IsSuspended)
{
Thread.Sleep(1000);
pDb.Refresh();
}
foreach (var secondary in secondaryServers)
{
AvailabilityDatabase sDb = secondary.AvailabilityGroups[agName].AvailabilityDatabases[dbName];
sDb.SuspendDataMovement();
while (!sDb.IsSuspended)
{
Thread.Sleep(1000);
sDb.Refresh();
}
sDb.LeaveAvailabilityGroup(); // this appears to be the equivalent of SET HADR OFF
Database db = secondary.Databases[dbName];
db.UserAccess = DatabaseUserAccess.Single;
secondary.KillAllProcesses(dbName);
db.Drop();
}
pDb.Drop();
Database db = primaryServer.Databases[dbName];
db.UserAccess = DatabaseUserAccess.Single;
primaryServer.KillAllProcesses(dbName);
db.Drop();
I am attempting to use SMO to write a simple utility to backup/restore databases. This works very well when there is only one point in time in the backup file. However when there is a backup file that has multiple backup points defined (not backup sets) SMO always chooses the earliest, whereas SSMS will always choose the latest.
This leads to an incorrect restore of the data, and I would like to figure out if there is a property that I can set that will force the Restore class to always use the latest backup point.
I have already tried to set the Restore.ToPointInTime but that won't work due to the recovery model of the database being simple.
I have found a MSDN article describing how to choose your restore time, and it includes setting the database to full recovery mode:
http://technet.microsoft.com/en-us/library/ms179451(v=sql.105).aspx
Is this necessary when using SMO, and is there a way to do it using pure SMO (no C# sql commands)? I have used the Restore.ReadBackupHeaders and from that I am able to extract the available backup points in time, but not able to set the one to restore anywhere.
EDIT:
Here is the code I am using, including a recent change which attempts to set the database recovery model via smo:
public void RestoreDatabase(string databaseName, string backupPath)
{
var server = new Server(GetServerConnection());
//If the database doesn't exist, create it so that we have something
//to overwrite.
if (!server.Databases.Contains(databaseName))
{
var database = new Database(server, databaseName);
database.Create();
}
var targetDatabase = server.Databases[databaseName];
targetDatabase.RecoveryModel = RecoveryModel.Full;
targetDatabase.Alter();
Restore restore = new Restore();
var backupDeviceItem = new BackupDeviceItem(backupPath, DeviceType.File);
restore.Devices.Add(backupDeviceItem);
restore.Database = databaseName;
restore.ReplaceDatabase = true;
restore.Action = RestoreActionType.Database;
var fileList = restore.ReadFileList(server);
var dataFile = new RelocateFile();
string mdf = fileList.Rows[0][1].ToString();
dataFile.LogicalFileName = fileList.Rows[0][0].ToString();
dataFile.PhysicalFileName = server.Databases[databaseName].FileGroups[0].Files[0].FileName;
var logFile = new RelocateFile();
string ldf = fileList.Rows[1][1].ToString();
logFile.LogicalFileName = fileList.Rows[1][0].ToString();
logFile.PhysicalFileName = server.Databases[databaseName].LogFiles[0].FileName;
restore.RelocateFiles.Add(dataFile);
restore.RelocateFiles.Add(logFile);
var backupHeaderInfo = GetBackupHeaderInformation(restore, server);
var latestBackupDate = backupHeaderInfo.Max(backupInfo => backupInfo.BackupStartDate);
restore.ToPointInTime = latestBackupDate.ToString();
server.KillAllProcesses(databaseName);
restore.SqlRestore(server);
}
It seems like this should do the trick, however the line
targetDatabase.RecoveryModel = RecoveryModel.Full
does not seem to do anything to change the recovery model, leading me to still get the following exception:
The STOPAT option is not supported for databases that use the SIMPLE recovery model.
RESTORE DATABASE is terminating abnormally.
EDIT 2:
I added the line
targetDatabase.Alter();
and it fixed the not updating problem. However It now restores but leaves the database in restoring mode, so it is unable to be queried.
EDIT 3:
I got the code working by setting the Restore.FileNumber property to be the maximum value of the positions in the BackupHeaders, which seems to do the trick, though I'm still unsure why the backup file has multiple backup headers, but only a single backup set.
The working code is below.
public void RestoreDatabase(string databaseName, string backupPath)
{
var server = new Server(GetServerConnection());
//If the database doesn't exist, create it so that we have something
//to overwrite.
if (!server.Databases.Contains(databaseName))
{
var database = new Database(server, databaseName);
database.Create();
}
var targetDatabase = server.Databases[databaseName];
targetDatabase.RecoveryModel = RecoveryModel.Full;
targetDatabase.Alter();
Restore restore = new Restore();
var backupDeviceItem = new BackupDeviceItem(backupPath, DeviceType.File);
restore.Devices.Add(backupDeviceItem);
restore.Database = databaseName;
restore.ReplaceDatabase = true;
restore.NoRecovery = false;
restore.Action = RestoreActionType.Database;
var fileList = restore.ReadFileList(server);
var dataFile = new RelocateFile();
dataFile.LogicalFileName = fileList.Rows[0][0].ToString();
dataFile.PhysicalFileName = server.Databases[databaseName].FileGroups[0].Files[0].FileName;
var logFile = new RelocateFile();
logFile.LogicalFileName = fileList.Rows[1][0].ToString();
logFile.PhysicalFileName = server.Databases[databaseName].LogFiles[0].FileName;
restore.RelocateFiles.Add(dataFile);
restore.RelocateFiles.Add(logFile);
var backupHeaderInfo = GetBackupHeaderInformation(restore, server);
restore.FileNumber = backupHeaderInfo.Where(backupInfo => backupInfo.BackupType == BackupType.Database).Max(backupInfo => backupInfo.Position);
server.KillAllProcesses(databaseName);
restore.SqlRestore(server);
targetDatabase.SetOnline();
}
Despite your saying that you don't have multiple backup sets, I think you do. From the documentation for the backupset table:
A backup set contains the backup from a single, successful backup operation.
So, if you have "multiple restore points" in a single backup file, you have multiple backup sets. Verify this by querying the dbo.backupset table in msdb
Pedantry aside, I think you're looking for the FileNumber property on the Restore object. This corresponds to the FILE = n backup set option in the T-SQL restore command. In order to get the last one, just pull the last row from your ReadBackupHeaders call.
To test for yourself, go through the motions of performing a restore through SSMS and then, instead of hitting "ok", hit the "Script" button near the top. I suspect that you'll see a FILE = <some number> in there somewhere.