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.
Related
Want to restore a database backup from linked server A to a database located on linked-server B using C#. prefer to use SMO.
I can restore from my local backup to local machine.
{
conn = new ServerConnection
{
ConnectionString = #"Data Source =
(localdb)\MSSQLLocalDb;Initial Catalog=master;Integrated Security=true",
};
try
{
//Restore Full
srv = new Server(conn);
//lsrv = srv.LinkedServers[#"DEVSQL\ALPHA"]; need to figure out how to restore to linked server instead of local.
//srv.KillAllProcesses("G4TestNew");
var res = new Restore();
res.Database = "G4TestNew";
res.Action = RestoreActionType.Database;
filePath = #"\\CABCSERVER\Database\Temp\Full.bak";
res.Devices.AddDevice(filePath, DeviceType.File);
res.ReplaceDatabase = true;
res.NoRecovery = true;
var dataFile = new RelocateFile("G4Test", #"C:\TBD\G4Test.mdf");
var logFile = new RelocateFile("G4Test_log", #"C:\TBD\G4TestNew.ldf");
res.RelocateFiles.Add(dataFile);
res.RelocateFiles.Add(logFile);
res.SqlRestore(srv);
}
EDIT(Adding more detail):.
In this case the linked servers are accessed via 'sql server authentication' and application does not have access to the credential required to connect directly and can use 'Integrated Security' to connect to localdb only.
In SMO you would not connect to one server and then administer a linked server. Instead connect directly to the target server. eg:
ConnectionString = #"Data Source =
DEVSQL\ALPHA;Initial Catalog=master;Integrated Security=true",
srv = new Server(conn);
Can anyone plz explain which special permission require for doing Microsoft.SqlServer.Management.Smo.Server connection via server and client both end.
i have following scenario.
server PC With Sql Server 2012 database
client PC With Only Program Which will connect database of server
i successfully got the db connection via visual stdio sqlconnectin class of remote database
but when i use Microsoft.SqlServer.Management.Smo.Server it will thrwo error for permission
can anyone help?
var connectionString = ConfigurationManager.ConnectionStrings["AppConString"].ConnectionString;
SqlConnectionStringBuilder sqlconstrbldr = new SqlConnectionStringBuilder(connectionString.ToString());
ServerConnection connection = new ServerConnection();
Microsoft.SqlServer.Management.Smo.Server sqlServer = new Microsoft.SqlServer.Management.Smo.Server(sqlconstrbldr.DataSource);
ServerName = sqlconstrbldr.DataSource;
foreach (Microsoft.SqlServer.Management.Smo.Database dbServer in sqlServer.Databases)
{
if (dbServer.Name.StartsWith("AMCS"))
{
string year = dbServer.Name;
year = year.Substring(4);
string Fyear = year.Substring(0, 2);
string Lyear = year.Substring(2);
Fyear = "20" + Fyear;
Lyear = "20" + Lyear;
string dbYear = Fyear + " - " + Lyear;
cmbDbYear.Items.Add(dbYear);
}
}
Explaination
cmbDbYear is Just Combobox in which all database names will be bonded.
MY Question is That What Remission Needed for SQL Server Management Object (Microsoft.SqlServer.Smo) must required for SQL Server Database user to successfully perform operation
i can Do Normal Database Operation currently but want also SQL Server Management Object (Microsoft.SqlServer.Smo) on database.
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).
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 writing a simple database backup and restore routine for an application. I can backup my database without issues, however when I restore is I am unable to gain exclusive access to my database.
I am trying all the combinations of fixes on SO, putting in single user mode, taking it offline then placing it back only with no success.
I can successfully restore the database within studio manager (express)
This method is the only connection to the SQL server at the time, so I don't understand why I can't perform the restore.
Appreciate the help to point out where the issue may be.
internal void RestoreDatabase(string databaseFile)
{
//get database details
var databaseConfiguration = new DatabaseConfiguration().GetDatabaseConfiguration();
try
{
//construct server connection string
var connection = databaseConfiguration.IsSqlAuthentication
? new ServerConnection(databaseConfiguration.ServerInstance,
databaseConfiguration.SqlUsername,
databaseConfiguration.SqlPassword)
: new ServerConnection(databaseConfiguration.ServerInstance);
//set database to single user and kick everyone off
using (
var sqlconnection =
new SqlConnection(new DatabaseConfiguration().MakeConnectionString(databaseConfiguration)))
{
sqlconnection.Open();
using (
var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET Single_User WITH Rollback IMMEDIATE",
sqlconnection))
{
sqlcommand.ExecuteNonQuery();
}
using (
var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET OFFLINE",
sqlconnection))
{
sqlcommand.ExecuteNonQuery();
}
using (
var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET ONLINE",
sqlconnection))
{
sqlcommand.ExecuteNonQuery();
}
sqlconnection.Close();
}
//setup server connection and restore
var server = new Server(connection);
var restore = new Restore();
restore.Database = databaseConfiguration.DatabaseName;
restore.Action = RestoreActionType.Database;
restore.Devices.AddDevice(databaseFile, DeviceType.File);
restore.ReplaceDatabase = true;
restore.Complete += Restore_Complete;
restore.SqlRestore(server);
}
catch (Exception ex)
{
//my bad
restoreDatabaseServerError(ex.InnerException.Message, EventArgs.Empty);
}
finally
{
//set database to multi user
using (
var sqlconnection =
new SqlConnection(new DatabaseConfiguration().MakeConnectionString(databaseConfiguration)))
{
sqlconnection.Open();
using (
var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET Multi_User",
sqlconnection))
{
sqlcommand.ExecuteNonQuery();
sqlcommand.Dispose();
}
sqlconnection.Close();
}
}
}
If anybody is connected to your database, SQL Server cannot drop it, so you have to disconnect existing connections, as you have tried. The problem with single_user is, that it still allows a single user to connect. As you yourself cannot be connected to the database when dropping it you have to get out of there. That opens up that slot for someone else to connect and in turn prevent you from dropping it.
There are a few SQL Server processes that are particularly good at connecting to a database in that split second. Replication is one example. (You shouldn't really drop a database that is published anyway, bat that is another story.)
So what can we do about this? The only 100% safe way is to prevent users from connecting to the database. The only practical way is to switch the database offline and then drop it. However, that has the nasty side effect, that SQL Server does not delete the files of that database, so you have to do that manually.
Another option is to just be fast enough. In your example you bring the database back online before you drop it. That is a fairly resource intensive process that gives an "intruder" lots of time to connect.
The solution I have been using with success looks like this:
ALTER DATABASE MyDb SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE;
USE MyDb;
ALTER DATABASE MyDb SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
USE tempdb;
DROP DATABASE MyDb;
This first sets the database to restricted user and connects to it. Then, while still connected it sets the database to single user. Afterwards the context is switched to tempdb and the drop is executed immediately thereafter. Important here is, to send these commands as one batch to SQL Server to minimize the time between the USE tempdb; and the DROP. Setting the database to restricted user in the beginning catches some rare edge cases, so leave it in even though it does not make sense at first glance.
While this still leaves a theoretical gap for someone else to get in, I have never seen it fail.
After the database is dropped you can run your restore as normal.
Good luck.
Your restore needs to take place on the same connection you set the DB server to single user mode.
In summary for the changes below, I moved the end of the using to below your restore code, and moved the close for the SQL connection to after the restore so it uses the same connection. Also removed set offline and online since they aren't needed. Can't test at the moment, so let me know if it works.
//set database to single user and kick everyone off
using (var sqlconnection = new SqlConnection(new DatabaseConfiguration().MakeConnectionString(databaseConfiguration)))
{
sqlconnection.Open();
using (var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET Single_User WITH Rollback IMMEDIATE",sqlconnection))
{
sqlcommand.ExecuteNonQuery();
}
//setup server connection and restore
var server = new Server(sqlconnection);
var restore = new Restore();
restore.Database = databaseConfiguration.DatabaseName;
restore.Action = RestoreActionType.Database;
restore.Devices.AddDevice(databaseFile, DeviceType.File);
restore.ReplaceDatabase = true;
restore.Complete += Restore_Complete;
restore.SqlRestore(server);
sqlconnection.Close();
}