I'm in the process of learning SQLite in C# and I've come up with some generic methods for finding and updating data. I would appreciate any pointers with the design of my methods and/or my SQL syntax.
FYI: I'm usingSystem.Data andMono.Data.SqliteClientfor C# Mono 2.0 (Unity3D).
Here is an example of the database used for this post. Let's also assume that I check for null prior to obtaining/updating data.
Info - Table name
| display_name | coins | total_score | | Michael | 100 | 1356 | | John | 50 | 904 |Below is myFindData method, which is used to lookup data in a specified table.
public object FindData (string tableName, string selectHeader, string whereHeader, object whereValue) { using (var dbConnection = new SqliteConnection ("URI=file:" + Application.dataPath + connection)) { dbConnection.Open (); using (var dbCommand = dbConnection.CreateCommand ()) { dbCommand.CommandText = string.Format ("SELECT {0} " + "FROM {1} " + "WHERE {2} = @whereValue", selectHeader, tableName, whereHeader); SqliteParameter whereParam = new SqliteParameter ("@whereValue", whereValue); dbCommand.Parameters.Add (whereParam); using (var dbReader = dbCommand.ExecuteReader ()) { while (dbReader.Read ()) { if (dbReader.GetValue (0) != null) return dbReader.GetValue (0); } } } } return null; }I attempted to have SQL Injection prevention parameters fortableName,selectHeader andwhereHeader, however, it kept throwing an error saying the syntax was wrong. I'm assuming this can't be done then.
That method can be used like so:
int coins = (int)FindData ("Info", "coins", "display_name", "Michael");
This attempts to obtain the value under columncoins, from the tableInfo where thedisplay_name column is equal toMichael
Next I have theUpdateData method. This is used to update/save the data in the specified table.
public void UpdateData (string tableName, string whereHeader, object whereValue, string setHeader, object setValue){ using (var dbConnection = new SqliteConnection ("URI=file:" + Application.dataPath + connection)) { dbConnection.Open (); using (var dbCommand = dbConnection.CreateCommand ()) { dbCommand.CommandText = string.Format ("UPDATE {0} " + "SET {1} = @setValue " + "WHERE {2} = @whereValue", tableName, setHeader, whereHeader); SqliteParameter setParam = new SqliteParameter ("@setValue", setValue); SqliteParameter whereParam = new SqliteParameter ("@whereValue", whereValue); dbCommand.Parameters.Add (setParam); dbCommand.Parameters.Add (whereParam); Debug.Log (dbCommand.CommandText); dbCommand.ExecuteNonQuery (); } }}This method can be used like so:
UpdateData ("Info", "display_name", character.displayName, "coins", character.coins);
This method attempts to update theInfo table, based on the columndisplayName, which equalscharacter.displayName, which then updates thecoins column based on thecharacter.coins variable
Can anyone provide any criticism/advice on these generic methods for saving and loading data from a Sqlite database.
2 Answers2
SqliteParameter setParam = new SqliteParameter ("@setValue", setValue); SqliteParameter whereParam = new SqliteParameter ("@whereValue", whereValue); dbCommand.Parameters.Add (setParam); dbCommand.Parameters.Add (whereParam);
instead of doing this, you could actually do this
dbCommand.Parameters.AddWithValue("@setParam", setValue);dbcommand.Parameters.AddWithValue("@whereValue", whereValue);that is how I would do with with SQL Server, I don't think it is any different with SqlLite.
also, I would do this a little differently as well
using (var dbConnection = new SqliteConnection ("URI=file:" + Application.dataPath + connection)){ dbConnection.Open (); using (var dbCommand = dbConnection.CreateCommand ()) {
I would instead write it like this
using (var dbConnection = new SqliteConnection ("URI=file:" + Application.dataPath + connection))using (var dbCommand = dbConnection.CreateCommand ()){ dbConnection.Open ();it looks a lot cleaner and removes some of the nesting.
also, try to limit what you do inside the using statements, to whatever you actually need in the using statements and not more than that.
Another thing, don't spread your string onto 3 lines like that, it looks messy.
pull your SQL statement out of the using statements, and make it a simple string, then pass it into the constructor of the dbCommand.
this:
public void UpdateData (string tableName, string whereHeader, object whereValue, string setHeader, object setValue){ using (var dbConnection = new SqliteConnection ("URI=file:" + Application.dataPath + connection)) { dbConnection.Open (); using (var dbCommand = dbConnection.CreateCommand ()) { dbCommand.CommandText = string.Format ("UPDATE {0} " + "SET {1} = @setValue " + "WHERE {2} = @whereValue", tableName, setHeader, whereHeader); SqliteParameter setParam = new SqliteParameter ("@setValue", setValue); SqliteParameter whereParam = new SqliteParameter ("@whereValue", whereValue); dbCommand.Parameters.Add (setParam); dbCommand.Parameters.Add (whereParam); Debug.Log (dbCommand.CommandText); dbCommand.ExecuteNonQuery (); } }}
should look more like this
public void UpdateData (string tableName, string whereHeader, object whereValue, string setHeader, object setValue){ var sql = string.Format ("UPDATE {0} SET {1} = @setValue WHERE {2} = @whereValue", tableName, setHeader, whereHeader); Debug.Log (sql.ToString()); using (var dbConnection = new SqliteConnection ("URI=file:" + Application.dataPath + connection)) using (var dbCommand = dbConnection.CreateCommand (sql, dbConnection)) { dbConnection.Open (); dbCommand.Parameters.AddWithValue("@setParam", setParam); dbcommand.Parameters.AddWithValue("@whereValue", whereValue); dbCommand.ExecuteNonQuery (); }}- \$\begingroup\$Thanks for the criticism! The only issue I have with this is my version of SQLite does not have
AddWithValue. So, unfortunately, I am stuck with creating them and adding them separately. In regards to cleaning my method up, the method you provided looks so much cleaner and I will take all of what you said into account when creating the rest of my methods. Do you think it is worth me following up on prepared statements?\$\endgroup\$Mike Hunt– Mike Hunt2016-03-11 20:45:39 +00:00CommentedMar 11, 2016 at 20:45 - 1\$\begingroup\$I am not sure that prepared statements are going to work for you with SQLite... you can look into it, it won't hurt to know about them either way.\$\endgroup\$Malachi– Malachi2016-03-11 21:12:31 +00:00CommentedMar 11, 2016 at 21:12
To address one aspect in particular:
I attempted to have SQL Injection prevention parameters for
tableName,selectHeaderandwhereHeader, however, it kept throwing an error saying the syntax was wrong. I'm assuming this can't be done then.
Good remark. Yes, this cannot be done, by design (read: it should not be done this way, ideally). What you are trying to do can seem like a good idea on the surface, to allow you to dynamically write an arbitrary SQL query on the fly by passing it DB object identifier names, and evenSQL executable code as object parameters.
However, this is avery bad idea in almost 100% of cases (with the very rare edge cases where you purposefullywant to inject SQL to execute, which should always be done withextreme prudence).
Here are a few reasons why you shouldn't use this method in production code:
Potential SQL injection vulnerability, obviously;
Queries are not compiled (which is part of what preparing statements does) and therefore ran "ad hoc" against the database each time;
Prepared statements must know about the database objects they run on before they are executed, safeguarding against SQL errors at compile time instead of throwing errors from the database at run time.
Compiled queries arere-usable so this will increase performance.
Debugging dynamic SQL queries is much more difficult than static queries.
Ideally, you would want to write prepared queries that are specific to a single purpose. This takes more time to write the code, but in the end you will be much better off for the reasons stated above.
You should read theSqlCommand.Prepare documentation. I also found an article by Visual Studio Magazine calledSQLite Performance and Prepared Statements which looks like it has some more vendor-specific functionality for SQLite.
As a thumb rule for what you actuallycan use as parameters for queries, see the@param in the following statements:
SELECT col1, col1 FROM table1 WHERE col1 = @param1;INSERT INTO table1 (col1, col2) VALUES (@param1, @param2);UPDATE table1 SET col1 = @param1 WHERE col2 = @param2;DELETE FROM table1 WHERE col1 = @param1;- 2\$\begingroup\$Well I added a comment before but I guess my internet was trashy and didn't post it. Thanks for your answer @PinCrash and I agree with what you said. I will be implementing my system based off yours and Malachi's answer so I thank you guys very much.\$\endgroup\$Mike Hunt– Mike Hunt2016-03-11 22:26:06 +00:00CommentedMar 11, 2016 at 22:26
You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.


