I have an application that saves and opens data (which is saved as SQL CE database file). Every time the project gets saved, a new .sdf file is generated with table structure defined by my code and I do not need to run any validation against it.
My concern is when user import (open) the .sdf file in a OpenFileDialog, there will be chance user may select a database file generated from a different application (i.e. having a different table schema). I would need to validate the importing database table schema or the application may crash if the wrong database file is opened and processed.
I do not need to compare schema between files. All I need is to check if the database file contain a certain table structure or table names (which I think should be sufficient for my purpose). What is the easiest way to do this?
[EDIT]
I used the following method to validate the database file, which works. I use a string array to checked against a SqlCeDataReader (which stores the Table name). It works but I wonder if there's an even easier way - is there a build in method in .NET to use?
using (SqlCeConnection conn = new SqlCeConnection(validateConnStr))
{
using (SqlCeCommand cmd = new SqlCeCommand(#"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES", conn))
{
try
{
conn.Open();
SqlCeDataReader rdr = cmd.ExecuteReader();
string[] tableArr = { "FirstTable", "SecondTable" };
int ta = 0;
while (rdr.Read())
{
if (rdr.GetString(0) != tableArr[ta])
{
isValidDbFile = false;
}
else
{
isValidDbFile = true;
}
ta++;
}
}
catch (Exception ex)
{
//MessageBox.Show(ex.ToString());
}
finally
{
conn.Close();
}
}
}
Open the database (make sure to have error handling for this, as the user can point to any file).
run: SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'MyTable'
If this returns data, your table is there.
Related
I am using Npgsql version 4.1.3.1 as a .NET data provider for PostgreSQL. Here is my use case
For a database I have to create table's dynamically and table name is Guid which I passing to one method.
If particular table is exist then using table specific connection string (each table has own credential), I need to insert data into table.
If table not exist then using admin connection string (has ONLY permission to create a table), I need to create the table and then insert the record in respective table.
Currently I am using C# try/catch, within try I am trying to insert the record in table and if table is NOT exist, then in catch I am trying to create the table and try re-insert again.
private async Task<bool> Insert(Guid tableId, string name)
{
//here connection string is specific to table, which
//don't have permission to create any table
using (var conn = await GetTableConnectionString(tableId))
{
//open site specific connection
await conn.OpenAsync();
try
{
//insert data in table
await using (var cmd = new NpgsqlCommand($#"Insert into Tbl-{tableId:N} (
id,
name) Values (#id, #name)", conn))
{
cmd.Parameters.AddWithValue("id", Guid.NewGuid());
cmd.Parameters.AddWithValue("name", name);
await cmd.ExecuteNonQueryAsync();
}
//close site specific connection
await conn.CloseAsync();
}
catch (Npgsql.PostgresException ex)
{
//close site specific connection
await conn.CloseAsync();
if (ex.SqlState == "42P01") // table does not exist
{
//here admin connection string have permission to create a table
using (var adminConn = await GetAdminConnectionString())
{
try
{
//open admin specific connection
await adminConn.OpenAsync();
//create a table
using (var cmd = new NpgsqlCommand($#"CREATE Table Site_{tableId:N} (
id UUID PRIMARY KEY,
name VARCHAR(128);", adminConn))
{
cmd.ExecuteNonQuery();
}
//close admin specific connection
await adminConn.CloseAsync();
//insert data to table again
return await Insert(tableId, name);
}
catch (Exception exception)
{
//close admin specific connection
await adminConn.CloseAsync();
throw;
}
}
}
}
}
return true;
}
Question is,
Is this approach correct? without exception handing (if (ex.SqlState == "42P01") // table does not exist) do we have any better way or ant library out of the box helps here? My connection string is also different for table and admin.
Given your specific design, trying and catching 42P01 seems reasonable. An alternate approach would be to first check if the table exists (by querying information_schema.tables or pg_class), but that adds an additional roundtrip and is potentially vulnerable to race conditions.
However, consider reviewing your design of dynamically created tables. If the tables have identical schema and the goal here is to have authorization per-table, then PostgreSQL has row-level security which may allow a much better design.
I created a sqlite database in unity... and tried to connect with this function.
void AddScores(string conn)
{
IDbConnection dbconn;
dbconn = (IDbConnection)new SqliteConnection(conn);
dbconn.Open();
using(IDbCommand dbCmd = dbconn.CreateCommand())
{
// string sqlQuery = "SELECT Id FROM PickAndPlace ";
string sqlQuery= "INSERT INTO PickAndPlace (Id) VALUES (324)";
dbCmd.CommandText = sqlQuery;
using(IDataReader reader = dbCmd.ExecuteReader())
{
while (reader.Read())
{
print(reader.GetInt32(0));
}
dbconn.Close();
reader.Close();
dbCmd.Dispose();
}
}
}
The following code not working if I try insert values...and it is showing this error "The database file is locked:
database is locked" But If I try select this works fine.So where is my mistake?
Sqlite generally accepts a single "connection". Once one application connects to the database, which means just acquiring a write lock on it, no other applications can access it for writes, but can access it for reads. Which is just the behaviour you are seeing. See File Locking And Concurrency Control in SQLite Version 3 for a bunch more details about how this works, the various locking states etc.
But in principle, you can only have a single connection open. So somehow you have more than one. Either you forget to close some connections, or multiple threads or applications are trying to modify it. Or perhaps some error occurred and left the locking files in a bad state.
I have a program which is supposed to open, edit, create and save access databases. For saving I copy an empty database with just the tables (just to avoid going through the hassle of creating every table and column etc) and try to fill it with values via the TableAdapterManager.UpdateAll method.
string _TemplateConnectString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};";
_connection = new OleDbConnection(string.Format(_TemplateConnectString, dlg.FileName));
_connection.Open();
DataSet1TableAdapters.TableAdapterManager tam=new TableAdapterManager();
tam.Connection = _connection;
try
{
tam.UpdateAll(dataset);
}
catch (System.Exception ex)
{
MessageBox.Show("Update failed");
}
It finishes with no exceptions but the values don't get inserted into the new database.
Also as far as I know the UpdateAll method only updates modified row so if I open some db and it inserts it's rows, it will not take them into account even though there are not in the database that I am trying to fill.
I have also tried filling the database with the ADODB and ADOX extensions but all the solutions I found with those was a lot of hardcoding and no regards for hierarchy, keys, etc.
Is there a way to force insert everything in the new database?
Is your template database in the Visual Studio project directory? It might have something to do with Visual Studio copying the database to the bin/debug or bin/release folder...
Try to use the right Data source database name, here an
example with an excel file:
cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\somepath\ExcelFile.xls;" & _
"Extended Properties=""Excel 8.0;HDR=Yes;"";"
A clumsy solution but it works. I iterate the tables of the dataset and save the via an sql string generator like this:
void SaveTable(DataTable dt)
{
string[] inserts;
try
{
inserts = SqlHelper.GenerateInserts(dt, null, null, null);
foreach (string s in inserts)
{
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = s;
cmd.Connection = _connection;
int n = cmd.ExecuteNonQuery();
}
}
catch (Exception e)
{
SaveOk = false;
}
}
I found the SqlHelper somewhere on this site, but completely lost where, unforunately. So here is the pastebin with it https://pastebin.com/iCMVuYyu
I'm writing a music player application using WPF (C#). As part of its functionality, I'm populating a music library, where I'm storing the Title and Path to an mp3 file. The user gets to select a root folder for his music library and then the contents are populated in a "Songs" table. This is the code that I've written:
private void Populate_Click(object sender, RoutedEventArgs e)
{
// Folder browser
FolderBrowserDialog dlg = new FolderBrowserDialog();
dlg.ShowDialog();
string DirectoryPath = System.IO.Path.GetDirectoryName(dlg.SelectedPath);
// Get the data directory
string[] A = Directory.GetFiles(DirectoryPath, "*.mp3", SearchOption.AllDirectories);
string[] fName = new string[A.Count()];
// Initialize connection
string connstr = "Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True";
SqlConnection conn = new SqlConnection(connstr);
conn.Open();
// Create the SqlCommand
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "InsertSongs";
// Create the parameters and execute the command
for (int i = 0; i < A.Count(); i++)
{
fName[i] = System.IO.Path.GetFileName(A[i]);
cmd.Parameters.AddWithValue("#Title", fName[i]);
cmd.Parameters.AddWithValue("#Path", A[i]);
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show("Error: " + ex.Message);
}
finally
{
listBox1.Items.Add(A[i]);
listBox2.Items.Add(fName[i]);
cmd.Parameters.Clear();
}
}
// Close the connection
cmd.Dispose();
conn.Close();
conn.Dispose();
}
The code for the stored procedure is simple -
ALTER PROCEDURE dbo.InsertSongs
(
#Title nvarchar(50),
#Path nvarchar(50)
)
AS
INSERT INTO Songs(Title, Path) VALUES(#Title, #Path)
Now, when I execute the program, there is no error message thrown (the file names and directory names have size less than 50). However, at the end of execution, no value is inserted in the Songs table.
The Songs table is described as below:
ID int
Title nvarchar(50)
Path nvarchar(50)
I'm not sure where I went wrong: I have also tried using SqlParameter and then defining the type of parameter as NVARCHAR with size 50, but to no avail. May I kindly request you to assist me here? Many thanks in advance.
The whole User Instance and AttachDbFileName= approach is flawed - at best! Visual Studio will be copying around the .mdf file and most likely, your INSERT works just fine - but you're just looking at the wrong .mdf file in the end!
If you want to stick with this approach, then try putting a breakpoint on the myConnection.Close() call - and then inspect the .mdf file with SQL Server Mgmt Studio Express - I'm almost certain your data is there.
The real solution in my opinion would be to
install SQL Server Express (and you've already done that anyway)
install SQL Server Management Studio Express
create your database in SSMS Express, give it a logical name (e.g. SongsDatabase)
connect to it using its logical database name (given when you create it on the server) - and don't mess around with physical database files and user instances. In that case, your connection string would be something like:
Data Source=.\\SQLEXPRESS;Database=SongsDatabase;Integrated Security=True
and everything else is exactly the same as before...
I got some data inputed by the user that should be added to a Database File (.sdf). I've choose Sql Server CE because this application is quite small, and i didn't saw need to work with a service based database.
Well any way.
Here goes the code:
public class SqlActions
{
string conStr = String.Format("Data Source = " + new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\basedados.sdf");
public SqlCeConnection SQLCEConnect()
{
SqlCeConnection Connection = new SqlCeConnection(conStr);
Connection.Open();
return Connection;
}
public Boolean AdicionarAuditorio(string Nome, int Capacidade)
{
string Query = "INSERT INTO auditorios (nome, capacidade) VALUES (#Nome, #Capacidade)";
using (var SQLCmd = new SqlCeCommand(Query, SQLCEConnect()))
{
SQLCmd.Parameters.AddWithValue("#Nome", Nome);
SQLCmd.Parameters.AddWithValue("#Capacidade", Capacidade);
if (SQLCmd.ExecuteNonQuery() == 1)
{
return true;
} else {
return false;
}
}
}
}
I use the AdicionarAuditorio(string Nome, int Capacidade) function to Insert the data. running ExecuteNonQuery() which is supposed to return the number of affected rows after he as run the query.
So it should return 1 if the query as successful, right?
In the end he returns 1, but if I browser the table data, the data that the query should add isn't there.
So whats wrong here?
NOTE. If your thinking that the
problem is the connection: I can't see
why is the problem once i got some
Select statements that use that
connection function SQLCEConnect()
and they all work pretty well.
Thanks in advance.
Are you sure you are looking at the right file? When you build your app in VS, it copies the SDF file as content to the target folder, so the database in your project will not reflect any updates. Your code is picking up the the file location there.
This is btw not a good practice, because once deployed, the program folders are not writable to your app (could this be the problem - did you already deploy?). Instead, the database file should reside in your appdata folder.
Is it possible that you make the call to AdicionarAuditorio in a TransactionScope without calling transactionScope.Complete()?