Creating SQL Server backup file (.bak) with c# to any location - c#

I'm trying to write simple application in C# which will allow me to backup, zip and send over ftp my SQL Server database.
One problem I have encountered is that I'm not able to create the backup file (.bak) if I try to do it in different location than "C:\Program Files\Microsoft SQL Server\MSSQL.3\MSSQL\Backup" or "C:\Program Files\Microsoft SQL Server\MSSQL.3\MSSQL\Data" folder. I understand that this is a premission problem. Could someone point me to the resources or write here a short snippet how to programmatically add such a permission to any folder on my system.
Regards
Kris

i assume you are running your programm as a scheduled task ... did you give writing permissions to the target folder for the executing user of the task??
edit:
with permissions you can have 2 scenarios:
windows authenification
mixed authentification
if you are using windows authentification, the read and write permissions of the windows user are taken. otherwise the permissions for the sql server service account.
and this behaviour makes sense to me and maybe hits the nail in your scenario!
edit 2:
i don't want to encourage you to do so ... some admins may hate you when you mess up their acl's
but this may do the trick
btw: Magnus Johansson already gave you a "try-this" link
no matter for which method you go - be sure to hand in the correct user (as descriped above!)
(for full history)
...
side-note:
i know this is not the exact answer to your question, but i would recommend you smo to generate backups ...
like
using Microsoft.SqlServer.Management.Smo;
var bdi = new BackupDeviceItem(/* your path inlcuding desired file */);
var backup = new Backup
{
Database = /* name of the database */,
Initialize = true
};
backup.Devices.Add(bdi);
var server = new Server(this.SqlServer);
try
{
backup.SqlBackup(server);
}
catch (Exception ex)
{
// * log or sth
}
you only have to care for the .dll's. take assemblies for the desired server version (some params/properties vary through different server versions)
more info here

Ok Guys, Magnus and dittodhole! Thanks a lot for your help. I have combined Magnus'es link to the article on setting up permisions on the folder together with some more research and finally I've got it :).
So reassuming, I'm using Smo, and to create a folder with proper permissions I have to look for the group instead of win32_Users. Here you go a short snippet if someone finds this post he can find it usefull:
string tempPath = Directory.CreateDirectory("C:\\path_to_your_folder").FullName;
//set permissions
SelectQuery sQuery = new SelectQuery("Win32_Group",
"Domain='" +
System.Environment.UserDomainName.ToString() +
"'");
try
{
DirectoryInfo myDirectoryInfo = new DirectoryInfo("C:\\path_to_your_folder");
DirectorySecurity myDirectorySecurity = myDirectoryInfo.GetAccessControl();
ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(sQuery);
foreach (ManagementObject mObject in mSearcher.Get())
{
string User = System.Environment.UserDomainName + "\\" + mObject["Name"];
if(User.StartsWith("your-machine-name\\SQL"))
{
myDirectorySecurity.
AddAccessRule(new FileSystemAccessRule(User,
FileSystemRights.FullControl,
AccessControlType.Allow));
}
}
myDirectoryInfo.SetAccessControl(myDirectorySecurity);
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
Again thanks everyone for your help! Stackoverflow rocks!

Here is a procedure is use for back up in C#.Hope it helps
public void BackupDatabase (string BackUpLocation, string BackUpFileName, string
DatabaseName, string ServerName )
{
DatabaseName = "[" + DatabaseName + "]";
string fileUNQ = DateTime.Now.Day.ToString() + "_" + DateTime.Now.Month.ToString() + "_" + DateTime.Now.Year.ToString() +"_"+ DateTime.Now.Hour.ToString()+ DateTime.Now .Minute .ToString () + "_" + DateTime .Now .Second .ToString () ;
BackUpFileName = BackUpFileName + fileUNQ + ".bak";
string SQLBackUp = #"BACKUP DATABASE " + DatabaseName + " TO DISK = N'" + BackUpLocation + #"\" + BackUpFileName + #"'";
string svr = "Server=" + ServerName + ";Database=master;Integrated Security=True";
SqlConnection cnBk = new SqlConnection(svr);
SqlCommand cmdBkUp = new SqlCommand(SQLBackUp, cnBk);
try
{
cnBk.Open();
cmdBkUp.ExecuteNonQuery();
Label1.Text = "Done";
Label2.Text = SQLBackUp + " ######## Server name " + ServerName + " Database " + DatabaseName + " successfully backed up to " + BackUpLocation + #"\" + BackUpFileName + "\n Back Up Date : " + DateTime.Now.ToString();
}
catch (Exception ex)
{
Label1.Text = ex.ToString();
Label2.Text = SQLBackUp + " ######## Server name " + ServerName + " Database " + DatabaseName + " successfully backed up to " + BackUpLocation + #"\" + BackUpFileName + "\n Back Up Date : " + DateTime.Now.ToString();
}
finally
{
if (cnBk.State == ConnectionState.Open)
{
cnBk .Close();
}
}
}

Take a look at this article.
Remember to set the permissions for the account that the SQL Server instance is running with.

Although this may not answer your immediate question, I'd advice you to look into SQL Server Integration Services (SSIS). This looks like the exact thing SSIS was created for, and in the 2008 version there's the possibility to use C# code if needed, should the standard components not do what you need (earlier versions used VB.NET).
MSDN SSIS Info Link 1
SSIS 2005 Tutorial Link 2
Take a look at it.

Related

How to grant user sys.admin role in SQL via Entity Framework?

Just to clear the wind I am aware off that granting users sysadmin in T-SQL in not a good thing but per customer requirements it needs to be done.
I have this :
public void AddUserToServer(List<string> users, List<string> servers)
{
foreach (var server in servers)
{
setConnection(server);
foreach (var user in users)
{
string sql =
"USE [master]" +
" CREATE LOGIN" + " [TP1\\" + user + "] FROM WINDOWS WITH DEFAULT_DATABASE=[master], " +
"DEFAULT_LANGUAGE=[us_english] " +
"EXEC sys.sp_addsrvrolemember #loginame = N'TP1\\" + user + "', #rolename = N'sysadmin'";
_dbContext.Database.ExecuteSqlCommand(sql);
}
}
}
I got the above script by creating the user manually in ssms and then generated the script. However when running it I am getting this current error:
'The procedure 'sys.sp_addsrvrolemember' cannot be executed within a transaction.
So I changed it to :
string sql =
"USE [master]" +
" CREATE LOGIN" + " [TP1\\" + user + "] FROM WINDOWS WITH DEFAULT_DATABASE=[master], " +
"DEFAULT_LANGUAGE=[us_english] " +
"GRANT ADMINISTER BULK OPERATIONS TO[TP1\\" + user + "]";
This creates the user BUT I do not get the sysadmin role.
How do I solve this?
So typical that I spent some time on this and finally after positing I find the answer.
I believe that this is a question more people will ask so I can provide what I found:
string sql =
"USE [master]" +
" CREATE LOGIN" + " [TP1\\" + user + "] FROM WINDOWS WITH DEFAULT_DATABASE=[master], " +
"DEFAULT_LANGUAGE=[us_english] " +
"EXEC sys.sp_addsrvrolemember #loginame = N'TP1\\" + user + "', #rolename = N'sysadmin'";
_dbContext.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sql);
NOTE:
TransactionalBehavior.DoNotEnsureTransaction
Starting with EF6 Database.ExecuteSqlCommand() by default will wrap
the command in a transaction if one was not already present. There are
overloads of this method that allow you to override this behavior if
you wish. Also in EF6 execution of stored procedures included in the
model through APIs such as ObjectContext.ExecuteFunction() does the
same (except that the default behavior cannot at the moment be
overridden).
Found the answer here

Access to the path * is denied

I have two WebServices that create files at the same director using the following method:
var schemaDir = _propClass.RepositorySettingRoot + #"\" + webServiceId;
if (!Directory.Exists(schemaDir))
{
Directory.CreateDirectory(schemaDir);
}
var schemaFile = schemaDir + #"\" + webMethodId + ".txt";
File.WriteAllText(schemaFile, webMethodSchema);
When trying to delete the file using File.Delete, if it was created by the 1st service it's deleted properly, but if it was created using the second, the exception Access to the path * is denied is raised.
The deletion code
schemaDir = _propClass.RepositorySettingRoot + #"\" + webServiceId + #"\" + webMethodId + ".txt";
if (File.Exists(schemaDir))
{
File.Delete(schemaDir);
}
I've found the answer ..
It's a security issue.
The file should be deleted by the service created it.
The deletion method was on ws1, so it can delete only the files it created.

Use Sharepoint Copy web service to copy folder and contents

I am trying to make a program that copies all of a Sharepoint folder's contents (all subfolders and files) into another Sharepoint folder. Both of these folders will be on the same Sharepoint site.
However, I am trying to do this remotely - if possible*. Therefore, I have tried using the Copy web service without success. The Copy web service appears to only work with copying files, not folders. In addition, I cannot determine a way to iterate through the folder's contents to copy everything - it will only copy one item.
Thank you for any insights or tips,
Scott
*From a custom CRM workflow activity
~~Edited for clarification~~
In the end, I decided to create my own custom web service in Sharepoint that I was able to successfully access from Microsoft CRM. If anyone is interested, I've pasted the C# code I used to copy the folder structure:
public String CopyFolderContents(String sourceURL, String folderURL, String destinationURL)
{
try
{
#region Copying Code
//Get the SPSite and SPWeb from the sourceURL
using (SPWeb oWebsite = new SPSite(sourceURL).OpenWeb())
{
//Get the parent folder from the folderURL
SPFolder oFolder = oWebsite.GetFolder(folderURL);
//Create a list of all files (not folders) on the current level
SPFileCollection collFile = oFolder.Files;
//Copy all files on the current level to the target URL
foreach (SPFile oFile in collFile)
{
oFile.CopyTo(destinationURL + "/" + oFile.Name, true);
}
//Create a list of all folders on the current level
SPFolderCollection collFolder = oFolder.SubFolders;
//Copy each of the folders and all of their contents
String[] folderURLs = new String[collFolder.Count];
int i = 0;
foreach (SPFolder subFolder in collFolder)
{
folderURLs[i++] = subFolder.Url;
}
for (i = 0; i < folderURLs.Length; i++)
{
SPFolder folder = collFolder[folderURLs[i]];
folder.CopyTo(destinationURL + "/" + folder.Name);
}
}
#endregion Copying Code
}
catch (Exception e)
{
#region Exception Handling
String Message;
if (e.InnerException != null)
Message = "MESSAGE: " + e.Message + "\n\n" +
"INNER EXCEPTION: " + e.InnerException.Message + "\n\n" +
"STACK TRACE: " + e.StackTrace + "\n\n" +
"Source: " + sourceURL + "\n" +
"Folder: " + folderURL + "\n" +
"Destination: " + destinationURL;
else
Message = "MESSAGE: " + e.Message + "\n\n" +
"STACK TRACE: " + e.StackTrace + "\n\n" +
"Source: " + sourceURL + "\n" +
"Folder: " + folderURL + "\n" +
"Destination: " + destinationURL;
throw new Exception(Message);
#endregion Exception Handling
}
return "Operation Successful!";
}
All I did was add this method into a Sharepoint web service and called it from CRM and it works.
Thanks to everyone who provided other answers,
Scott
There is a simple solution to this problem , as its just about copy and paste use the simple XCOPY command
XCOPY Copy files and/or directory trees to another folder. XCOPY is similar to the COPY command except that it has additional switches to specify both the source and destination in detail.
To copy a folder including all subfolders
XCOPY C:\utils\* D:\Backup\utils /s /i
here /i defines the destination as a folder
for more detail please refer this link

ExecuteNonQuery() not saving any record

I'm working a WinForms based C# tool which has an attached MDF file based database. I'm trying to use the SqlCommand.ExecuteNonQuery() method to save a record to this attached MDF database, but the record is not saved. No error or exception occurs; only problem is that the record is not actually saved.
I have a Console.WriteLine at the top which shows the query I'm trying to run. Its correct syntax-wise, and if I copy-paste it from the output windows and run it separately, it works.
I have correctly defined the connection string as the following, and it works fine for fetching records:
public static String connectionString = #"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\TestBuildDB.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
Here the function I'm using to save records:
public static void PerformDBWriteTransaction(string inputSQLStatement)
{
Console.WriteLine(inputSQLStatement);
DataTable returnDataTable = new DataTable();
SqlConnection sqlConnection = new SqlConnection();
sqlConnection.ConnectionString = connectionString;
SqlCommand cmd = new SqlCommand();
cmd.Connection = sqlConnection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = inputSQLStatement;
cmd.Connection.Open();
try
{
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
errorMessages.Clear();
errorMessages.Append("The following errors were found in the SQL statement:\n\n");
for (int i = 0; i < ex.Errors.Count; i++)
{
errorMessages.Append("Index #" + i + "\n" +
"Message: " + ex.Errors[i].Message + "\n" +
"LineNumber: " + ex.Errors[i].LineNumber + "\n" +
"Source: " + ex.Errors[i].Source + "\n" +
"Procedure: " + ex.Errors[i].Procedure + "\n");
}
MessageBox.Show(errorMessages.ToString());
}
finally
{
cmd.Connection.Close();
}
}
Can someone tell me what might be the problem ? Do I need to perform a 'commit' somehow ?
EDIT:
I have found the problem, and have written up a solution below .. Thanks to all who helped me out though :)
I found the problem ! It was very simple, and it was stupid really :) .. The code above is all correct .. Yes, people pointed out optimizations, etc, but still the code above was correct.
The problem was that when I imported the TestDB.MDF file into my Visual 2010 project, a copy of that was made inside the project's folder. When you run/debug the program, another copy of the this file is made and is put in the \bin\Debug\ folder. In the connection string I was using, I had mentioned: AttachDbFilename=|DataDirectory|\TestBuildDB.mdf .. This meant that all reads/writes were done to the copy in the bin\Debug folder. However, the TestDB.MDF file I was looking into to verify if records were inserted or not, was in the project's folder ! So basically, there were two MDF files, and I was writing the records into one file, but was trying to find them in the other :)
When you add an MDF file into your VS2010 Project, VS2010 by default makes a connection to that MDF file, from where you can browse the stuff in that MDF file .. The MDF file used for this purpose was the one placed in the project's folder, NOT the one in bin\Debug\ folder. And like I said earlier, my code used the one in the bin\Debug folder :)
So what I've done now is that I've removed the Test.MDF file reference from my project, which removes the copy present in the project's folder. However, I DO have a copy of TestDB.MDF file in the bin\Debug\ folder, which I connect to from within my application. And if I want to browse the MDf file outside my project, I use SQL Management Studio. The only problem here is that an MDF file can only be used by one program at a given time. So if I have to use it with my application, I have to take it offline from SQL Management studio, and vica versa !
I hope this explanation helps someone out there :)
The solution to this problem is very simple just give the full path of the original MDF file in the connection String like this:
connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=**C:\VISUAL STUDIO 2012\PROJECTS\ENGLISHTOHINDIDICTIONARY\ENGLISHTOHINDIDICTIONARY\DICTIONARY.MDF**;Initial Catalog=Dictionary;Integrated Security=false"
providerName="System.Data.SqlClient"
That's it, your problem is solved.
I had the same challenge, I simply changed the database property "Copy to Output Directory" from "Copy always" to "Do not copy" then moved my database.mdf (drag & drop from my IDE) into the bin\debug folder.
Tip:
The bin directory is normally hidden, use the "Show All Files" to display it
Provide a catch clause for all Exceptions. If there something wrong other than SqlException, you will never see what is it and your db will neved be updated. Imagine there is a FormatException...
Also check the return of ExecuteNonQuery : it's the number of rows affected by the query.
First, you should always wrap up your IDisposable objects in a using to ensure they're closed and disposed of properly (and that connection pooling can do its thing). Second, when modifying data wrap up your sql in a transaction to maintain data integrity.
Try the following code and see if any exceptions are raised. I wouldn't normally recommend catching Exception as it's too general, I'd let that bubble up to the calling mechanism and handle it there - but for this instance it will show you any and all issues. I think your particular issue is at the .Open stage of the connection, so try stepping through.
public static void PerformDBWriteTransaction(string inputSQLStatement)
{
DataTable returnDataTable = new DataTable();
try
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlTransaction sqlTrans = sqlConnection.BeginTransaction())
{
try
{
using (SqlCommand cmd = new SqlCommand(inputSQLStatement, sqlConnection))
{
cmd.CommandType = CommandType.Text;
cmd.Transaction = sqlTrans;
cmd.ExecuteNonQuery();
}
}
catch (SqlException sqlEx)
{
sqlTrans.Rollback();
throw sqlEx;
}
sqlTrans.Commit();
}
}
}
catch (Exception ex)
{
errorMessages.Clear();
errorMessages.Append("The following errors were found in the SQL statement:\n\n");
for (int i = 0; i < ex.Errors.Count; i++)
{
errorMessages.Append("Index #" + i + "\n" +
"Message: " + ex.Errors[i].Message + "\n" +
"LineNumber: " + ex.Errors[i].LineNumber + "\n" +
"Source: " + ex.Errors[i].Source + "\n" +
"Procedure: " + ex.Errors[i].Procedure + "\n");
}
MessageBox.Show(errorMessages.ToString());
}
}
Hi I am working on library database when I add student record executionNonQuery
shows error like invalid column name page opens but saving data from not happening.
Here I have given the code statement
public partial class add_student_info : Form
{
SqlConnection con = new SqlConnection(#"Data Source=DESKTOP-SPT6GLG\SQLEXPRESS;Initial Catalog=library_managment;Integrated Security=True;Pooling=False");
public add_student_info()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
con.Open();
SqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "insert into student_info values('" + textBox1.Text + "','" + textBox2.Text + "','" + textBox3.Text + "','" + textBox4.Text + "'," + textBox5.Text + "," + textBox6.Text + "," + textBox7.Text + ")";
cmd.ExecuteNonQuery();
con.Close();
MessageBox.Show("Student recorc addedd sussfully");
}
}
}

Creating a database programmatically in SQL Server

How can I create a database programmatically and what is the minimum information I need to do this?
Please no "SQL Server Management Object API " suggestions.
You can either use the SQL Server Management Object API (see task "creating, altering and removing databases"):
var srv = new Server();
var db = new Database(srv, "mydb");
db.Create();
Information on how to get started is here. During SQL server installation you need to install the client SDK, the SMO assemblies are in C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies
Or if you don't want the dependency on these assemblies, you can also simply run DDL statements using ADO.Net (e.g. see this question):
using (var connection = new SqlConnection(myConnectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "CREATE DATABASE mydb";
command.ExecuteNonQuery();
}
Obviously you need a correct connection string: known sql server instance and a user with CREATE DATABASE permission.
Create database 'Databasename'
From the creators:
// your connection string
string connectionString = "Server=(local)\\netsdk;uid=sa;pwd=;database=master";
// your query:
var query = GetDbCreationQuery();
var conn = new SqlConnection(connectionString);
var command = new SqlCommand(query, conn);
try
{
conn.Open();
command.ExecuteNonQuery();
MessageBox.Show("Database is created successfully", "MyProgram",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
if ((conn.State == ConnectionState.Open))
{
conn.Close();
}
}
To create in default location with default settings, just:
static string GetDbCreationQuery()
{
// your db name
string dbName = "MyDatabase";
// db creation query
string query = "CREATE DATABASE " + dbName + ";";
return query;
}
Or, to create it in a specific location:
static string GetDbCreationQuery()
{
// your db name
string dbName = "MyDatabase";
// path to your db files:
// ensure that the directory exists and you have read write permission.
string[] files = { Path.Combine(Application.StartupPath, dbName + ".mdf"),
Path.Combine(Application.StartupPath, dbName + ".ldf") };
// db creation query:
// note that the data file and log file have different logical names
string query = "CREATE DATABASE " + dbName +
" ON PRIMARY" +
" (NAME = " + dbName + "_data," +
" FILENAME = '" + files[0] + "'," +
" SIZE = 3MB," +
" MAXSIZE = 10MB," +
" FILEGROWTH = 10%)" +
" LOG ON" +
" (NAME = " + dbName + "_log," +
" FILENAME = '" + files[1] + "'," +
" SIZE = 1MB," +
" MAXSIZE = 5MB," +
" FILEGROWTH = 10%)" +
";";
return query;
}
Even in case the execution fails, give it another try. The db files might have got created.
You need to open a connection to the server, i.e. you need a server and instance name.
You also need the proper access rights to create a database, so you might need some user name and password depending on the authentication settings on the server.
From the server name and authentication information you can construct a connection string and open a connection.
Then you can use the CREATE DATABASE SQL command (see here on MSDN). The only needed parameter for this command is a database name.
You need connection information: server, possibly instance, a user having create database rights on that server/instance and the corresponding password. Then you can use SMO for creating the database. Here is a small PowerShell example that you can very easily "translate" to C#, for example:
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
$s = New-Object Microsoft.SqlServer.Management.Smo.Server($ServerInstance)
# Instantiate the database object and add the filegroups
$db = New-Object Microsoft.SqlServer.Management.Smo.Database($s, $DatabaseName)
$primaryFG = New-Object Microsoft.SqlServer.Management.Smo.FileGroup($db, 'PRIMARY')
$db.FileGroups.Add($primaryFG)
# Create Data file
$syslogname = $DatabaseName + '_SysData'
$dbdsysfile = New-Object Microsoft.SqlServer.Management.Smo.DataFile($primaryFG, $syslogname)
$primaryFG.Files.Add($dbdsysfile)
$dbdsysfile.FileName = $s.MasterDBPath + '\' + $syslogname + '.mdf'
$dbdsysfile.Size = [double](5.0 * 1024.0)
$dbdsysfile.GrowthType = 'KB'
$dbdsysfile.Growth = 10000.0
$dbdsysfile.IsPrimaryFile = 'True'
# Create Log file
$loglogname = $DatabaseName + '_Log'
$dblfile = New-Object Microsoft.SqlServer.Management.Smo.LogFile($db, $loglogname)
$db.LogFiles.Add($dblfile)
$dblfile.FileName = $s.MasterDBLogPath + '\' + $loglogname + '.ldf'
$dblfile.Size = [double](10.0 * 1024.0)
$dblfile.GrowthType = 'KB'
$dblfile.Growth = 10000.0
# Create database with READ_COMMITTED_SNAPSHOT isolation level.
# Other options can be set on $db object before calling Create.
$db.IsReadCommittedSnapshotOn = $true
$db.RecoveryModel = [Microsoft.SqlServer.Management.Smo.RecoveryModel]::Simple
$db.Create()
Assuming you have the rights to fire off a CREATE DATABASE statement you can do so as you would any other query.
I should stress that being able to do so requires quite high privileges on the server and this would be restricted to DBAs in QA and Production environments.
For that reason I would make sure that your connection uses Windows Integrated Security. That way when the appropriate DBA runs your application the app will function as requested.
Once you have created your database you will also need to fire off the T-SQL to create logins and create users. I'm taking it as obvious that CREATE TABLE/VIEW statements will be needed.

Categories

Resources