How do you delete a generated mdf file with open connections? - c#

I have created a database with this code:
public static void CreateDatabase(string databasePath)
{
AppDomain.CurrentDomain.SetData("DataDirectory", databasePath);
using (var connection = new System.Data.SqlClient.SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;Initial Catalog=master; Integrated Security=true;;"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText =
String.Format("CREATE DATABASE {0} ON PRIMARY (NAME={0}, FILENAME='{1}')", "CoolDatabase", databasePath + #"\database.mdf");
command.ExecuteNonQuery();
command.CommandText =
String.Format("EXEC sp_detach_db '{0}', 'true'", "CotanDB");
command.ExecuteNonQuery();
}
connection.Close();
}
}
Which creates a nice working .mdf file for my unit tests. However, after running all tests on it, I want to remove it again so it doesn't take up space.
I tried this:
public static void DestroyDatabase(string databasePath)
{
if (File.Exists(databasePath + #"\database.mdf"))
{
File.Delete(databasePath + #"\database.mdf");
}
if (File.Exists(databasePath + #"\database_log.ldf"))
{
File.Delete(databasePath + #"\database_log.ldf");
}
}
But that throws an error
The process cannot access the file 'path to the database\database.mdf'
because it is being used by another process.
So I tried to close all connections to my database so I could drop it and delete the file. However, this does not work:
var server = new Server();
server.KillDatabase(databasePath + #"\database.mdf");
Which throws
Failed to connect to server ..
How do I destroy my local database file?
Edit: I tried the code in the answer in the comments:
using (var connection = new System.Data.SqlClient.SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;Initial Catalog=master; Integrated Security=true;;"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText =
String.Format("USE master");
command.ExecuteNonQuery();
command.CommandText =
String.Format("ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE", "CotanDB");
command.ExecuteNonQuery();
command.CommandText =
String.Format("DROP DATABASE {0}", "CoolDatabase");
command.ExecuteNonQuery();
}
connection.Close();
}
Followed by the same File.Delete code. But this throws:
User does not have permission to alter database "CoolDatabase", the
database does not exist, or the database is not in a state that allows
access checks.

Figured it out myself. If you call SqlConnection.ClearPool(connection), all connections to your mdf file are dropped, after which you can easily delete it.
Looking back at this, this is a horrible solution and should be avoided, as this construction means your unit tests are now dependent on an external database. Good luck trying to run this in an Azure Pipeline.
If you have the option, use an EntityFramework InMemory database:
https://learn.microsoft.com/en-us/ef/core/providers/in-memory/?tabs=dotnet-core-cli

Related

SQL server backup to shared folder

I'm trying to make a backup to a network shared folder with the following code:
string filePath = ("\\pc-usuario\folder\backup\backup.bak")
string connectionString = String.Format(#"Data Source={0};Initial Catalog={1};Integrated Security=True;MultipleActiveResultSets=True", server, database);
using (var connection = new SqlConnection(connectionString))
{
var query = String.Format("BACKUP DATABASE [{0}] TO DISK='{1}'", database, filePath);
using (var command = new SqlCommand(query, connection))
{
connection.Open();
command.CommandTimeout = 1800;
command.ExecuteNonQuery();
}
}
Got the following error:
The backup device cannot be opened. Operating system error 5(Access denied.).
If I try it with SQLExpress the same error happens. What am I missing?
You havent given the database backup a file name, only a file path
E.g. the final code should read
backup database wibble to disk = '\\pc-usuario\folder\backup\wibble.bak'

sqlite insert with c#

i am testing my sqlite local server with c#, I have the connection and query setup without problem. I tried to copy the query to sqlite and it runs without problem. However, when I run it in my program, nothing insert into the db. Wondering what the problem is.
I have set the db build action to Content, and the copy to output directory options to copy if newer
private void insertIntoDB(string query)
{
using (System.Data.SQLite.SQLiteConnection conn = new System.Data.SQLite.SQLiteConnection("data source=.\\VHTDatabase.db"))
{
using (System.Data.SQLite.SQLiteCommand cmd = new System.Data.SQLite.SQLiteCommand(conn))
{
conn.Open();
Console.WriteLine(query);
cmd.CommandText = query;
cmd.ExecuteNonQuery();
conn.Close();
}
}
}
Try a full path to your database in your Connectionstring
Then maybe try to add the CommandText before you open the Connection.
i mean:
cmd.CommandText = query;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
Does your database give you informations with SELECT?
Maybe you need to reconfigure the User of the Database and give him rights to write, change and delete.
If nothing helps, then you can check, if it gives you an error.
try
{
cmd.CommandText = query;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
catch(Exception ex)
{
Console.WriteLine(ex);
}

How to connect SQLite with my C# application

i have a problem with making a local database into my c# project and creating it..
I tried first with making a Microsoft Sql Server but the problem is that i need to make app which should run on every pc. The app should input data from user , and collect it to the database, and on every start of program, the database should be filled with the leftover of earlier input.. What you suggest me to do?
First to connect your c# application with sqlite you should start with getting connection string
private static string executableLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
private static string oldconnectionstring = Path.Combine(executableLocation, "YourDB.db");
private static string connectionString = "Data Source =" + oldconnectionstring.ToString();
After getting connection, to add your input to database follow below steps
using (SQLiteConnection conn = new SQLiteConnection(connectionString))
{
//Open connection to DB
conn.Open();
//Query to be fired
string sql = "Your Query to insert rows";
//Executing the query
using (SQLiteCommand cmd = new SQLiteCommand(sql, conn))
{
//Executing the query
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
//Close connection to DB
conn.Close();
}

LocalDB changes persist in different mdf files - Why?

I am trying to rebuild an application that originally used sqlite to now use 'localdb'. (I want an application that can create its own database locally and at runtime without requiring a pre-installed instance of sql server or sql express on the target machine)
I want to move away from using a 'third party' library (sqlite) as experience has told me it can be a pain to get it working from scratch, and towards something supposedly more straightforward to get up and running from scratch.
Using code copied (and slightly modified) from the web I have managed to create an mdf file dynamically/programmatically, but I am puzzled by what happens if I run it more than once, even if I choose a new filename each time. Namely it seems to somehow keep the changes/additions made on each run. Below is the relevant code...
public partial class Form1 : Form
{
SqlConnection conn;
public void CreateSqlDatabase(string filename)
{
string databaseName =
System.IO.Path.GetFileNameWithoutExtension(filename);
conn = new SqlConnection(
String.Format(
#"Data Source=(LocalDB)\v11.0;Initial Catalog=master;Integrated Security=True"
));
conn.Open();
using (var command = conn.CreateCommand())
{
command.CommandText =
String.Format(
"CREATE DATABASE {0} ON PRIMARY (NAME={0}, FILENAME='{1}')"
, databaseName, filename);
command.ExecuteNonQuery();
command.CommandText =
String.Format("EXEC sp_detach_db '{0}', 'true'", databaseName);
command.ExecuteNonQuery();
}
conn.Close();
}
private void button1_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
CreateSqlDatabase(openFileDialog1.FileName);
}
}
private void button2_Click(object sender, EventArgs e)
{
conn.Open();
SqlCommand comm = conn.CreateCommand();
comm.CommandText =
"create table mytable (id int, name nvarchar(100))";
comm.ExecuteNonQuery();
comm.CommandText =
"insert into mytable (id,name) values (10,'testing')";
comm.ExecuteNonQuery();
comm.CommandText = "select * from mytable";
SqlDataReader reader = comm.ExecuteReader();
while (reader.Read())
{
textBox1.Text +=
reader["id"].ToString() + ", " + reader["name"].ToString() + "\r\n";
}
conn.Close();
}
}
If I run the app once It runs through fine.
If I run the app a second time, and choose a different filename for the database it tells me 'mytable' already exists.
If I comment out the create table code it runs, but the select query returns multiple rows indicating multiple inserts (one for each time the app runs)
I am just seeking to understand why this happens. Do I need to delete database/table each time if I want the app to behave as if it has created the database/table from scratch on each subsequent run?
You have initial catalog 'master' in your connection string. Are you sure you haven't created the tables in the master database instead of the newly created database?
After the creation & detach of the database file, you could try and change your connection to:
SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\v11.0;Integrated Security=True;AttachDbFilename=c:\xxx\xxx\xxx.mdf");

Create an SQL Express 2008 database in C# code, but login fails when trying to connect with a sysadmin

I have a piece of code that creates an SQL Server Express 2008 in runtime, and then tries to connect to it to execute a database initialization script in Transact-SQL. The code that creates the database is the following:
private void CreateDatabase()
{
using (var connection = new SqlConnection(
"Data Source=.\\sqlexpress;Initial Catalog=master;" +
"Integrated Security=true;User Instance=True;"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText =
"CREATE DATABASE " + m_databaseFilename +
" ON PRIMARY (NAME=" + m_databaseFilename +
", FILENAME='" + this.m_basePath + m_databaseFilename + ".mdf')";
command.ExecuteNonQuery();
}
}
}
The database is created successfully. After that, I try to connect to the database to run the initialization script, by using the following code:
private void ExecuteQueryFromFile(string filename)
{
string queryContent = File.ReadAllText(m_filePath + filename);
this.m_connectionString = string.Format(
#"Server=.\SQLExpress; Integrated Security=true;Initial Catalog={0};", m_databaseFilename);
using (var connection = new SqlConnection(m_connectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = queryContent;
command.CommandTimeout = 0;
command.ExecuteNonQuery();
}
}
}
However, the connection.Open() statement fails, throwing the following exception:
Cannot open database "TestData"
requested by the login. The login
failed. Login failed for user
'MYDOMAIN\myusername'.
I am completely puzzled by this error because the account I am trying to connect with has sysadmin privileges, which should allow me to connect any database (notice that I use a connection to the master database to create the database in the first place).
Is there a reason you specify User Instance=True when you create it but not when you try to connect to it?
When you create it after connecting with User Instance, it will create the database files but does not attach it to your actual instance. You'll either have to not specify User Instance=True in the first connection string or add it to the second and specify the database file to use.
Is the user you are logging with have rights to the database 'TestData'?
If not grant the user the privileges required.
I am not sure if this means anything, but in your first create you are connecting to server
.\\sqlexpress
The second one is
.\SQLExpress
You'll need to issue a CREATE USER command (see: http://msdn.microsoft.com/en-us/library/ms173463.aspx) after creating the database but before trying to open a connction to that database.
For example:
CREATE USER 'MYDOMAIN\myusername' FOR LOGIN 'MYDOMAIN\myusername'

Categories