I've inherited an application with a lot of ADO work in it, but the insert/update helper method that was written returns void. We've also been experiencing a lot of issues with data updates/inserts not actually happening. My goal is to update all of them to check rows affected and depending on the results, act accordingly, but for the time being of finding what may be causing the issue, I wanted to log SQL statements that are called against the server and the number of rows affected by the statement.
This is the statement I'm attempting:
SqlCommand com = new SqlCommand(String.Format("'INSERT INTO
SqlUpdateInsertHistory(Statement, AffectedRows) VALUES (''{0}'', {1});'",
statement.Replace("'", "''"), rows), con);
but it seems to constantly break somewhere in the sql that is being passed in (some cases on single quotes, but I imagine there are other characters that could cause it as well.
Is there a safe way to prep a statement string to be inserted?
I just can't rightly propose a solution to this question without totally modifying what you're doing. You're currently wide open to SQL Injection. Even if this is a local application, practice how you want to play.
using (SqlCommand com = new SqlCommand("INSERT INTO SqlUpdateInsertHistory(Statement, AffectedRows) VALUES (#Statement, #AffectedRows)", con))
{
com.Parameters.AddWithValue("#Statement", statement);
com.Parameters.AddWithValue("#AffectedRows", rows);
com.ExecuteNonQuery();
}
Have you tried SQL Server Profiler? It's already been written and logs queries, etc.
Someone else tried this and got a lot of decent answers here.
Related
I have a problem in my program that's supposed to store projects given by the user in a database. I'm stuck on the edit project button. After entering new values in the program and hitting the button to save the values everything runs successfully with no errors. The messagebox that says "project edited" appears, I hit ok but the database stays the same. There is no error in the code, the SQL code that gets sent to update the database values is also correct but it doesn't work. Can anyone help with this because I am lost.
Here is the method that creates and executes the SQL code to update the database.
enter image description here
Wow man that code is wrong in so many ways according to code standards and principles most popular :) but that is not what the question is about directly, though getting you past lost we have to start at the basic tbh:
Suggestions
when you catch that exception if it comes, show that in a messagebox also you can even add an error icon as part of the .Show command, it's build in.
Move the connection.Close to the finally block instead of having it replicated
Consider making an SQL procedure instead and just parse the parameter into that, this code is prone to sql injection that you pose
Consider not making the procedure and familiarize Yourself with entity framework, it's going to make your life so much easier
do not concatenate like that, use interpolation or string.Combine or you'll be copying stuff all around on the stack, for each + a new copy one and two into third, it is super inefficient
When You write the code works and the sql is correct, consider that the outcome is not the desired and therefore it doesn't technically ;) the best and the worst about computers, is that they do what you ask.
Don't write Your DAL code in the form at all
Consider checking your parameters for default values
You do not have data to say 'project was updated' only 'values were saved', you do not check the old values in the code
Still besides that I do not see why what you wrote wouldn't work, provided the resulting sql is valid in what db you use, but i suppose if you do some of these things the error will present itself
I don't think it's a connection problem because I have a function that updates only the finishdate of the project and that works completely fine.
Here is the function:
public static MySqlCommand FinishProject(int projID, string finishdate) {
try {
if (connection != null) {
connection.Open();
cmd = connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#value", projID);
cmd.Parameters.AddWithValue("#finishdate", finishdate);
cmd.CommandText = "UPDATE `b1c`.`projects` SET `finishdate` = (#finishdate) WHERE (`projectid` = (#value));";
int i = cmd.ExecuteNonQuery();
connection.Close();
if (i != 0) {
MessageBox.Show("Project finalized.");
i = 0;
}
}
} catch (Exception ex) {
MessageBox.Show("Catch");
connection.Close();
}
return cmd;
}
You can see it's basically the same the only difference are the values.
So it shouldn't be a connection thing because this one works fine I think.
I also don't think it's a problem in the SQL database because all the problems
I've had up until now that had anything to do with the database have shown as errors in visual studio.
If anyone can help I will provide screenshots of anything you need and thank you all once again for trying to help.
Here is the screenshot of the function I've pasted previously so it's easier to look at.
finishprojectfunction
I have a long set of SQL scripts. They are all update statements. It's for an access database. I want to validate the script before I run it. Firstly, I'd like to make sure that the query can be parsed. I.e. that the SQL is at least syntactically correct. Secondly, I'd like to make sure that the query is valid in terms of database structure - i.e. there are no missing columns or the columns are of the wrong type etc. But, I don't want the query to be actually executed. The aim of this is to do a quick validation before the process kicks off because the process takes several hours and one syntactical error can waste a day of someone's time.
I will probably write the tool in C# with .net but if there's a pre-built tool that would be even better. I will probably use the Access API. In SQL Server this is very straight forward. You can just validate the query in SQL Server management studio before running it. It will give you a good indication of whether the SQL will complete or not.
How would I go about doing this?
Edit: an answer below solves the issue of checking syntax. However, I'd still like to be able to validate the semantic content of the query is OK. However, I think this might be impossible in Access without actually running the query. Please tell me I'm wrong.
I'm not 100% sure if Access works the same way as a traditional database, but with a mainstream RDMBS, there are actually three distinct steps that happen when you run a query:
Prepare
Execute
Fetch
Most are oblivious to the distinction because they just hit "run" and see results come back.
It's the "Execute" that actually compiles the statement before going off and pulling data.
When you use ADO, you can actually see the three events as three separate calls to the database. What this means is you can trap the execute step to see if it fails, and if it succeeds, there is nothing requiring you to actually get the results.
OleDbConnection conn = new OleDbConnection();
conn.ConnectionString = String.Format("{0}{1}",
#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=", #"c:\Access\MyDb.accdb");
conn.Open();
bool valid;
using (OleDbCommand cmd = new OleDbCommand("select [Bad Field] from [Table]", conn))
{
try
{
OleDbDataReader reader = cmd.ExecuteReader();
valid = true;
reader.Close(); // Did not ever call reader.Read()
}
catch (Exception ex)
{
valid = false;
}
}
And now valid indicates whether or not the statement compiled.
If you want to get really fancy, you can parse the exception results to find out why the command failed.
Access supports transactions on its Connection object. Try to execute your SQL statement inside a transaction and always call Rollback. Wrap the whole attempt in a Try/Catch block to assess whether the statement executed successfully or not.
I looked at lots of questions but evidently my SO-fu isn't up to the task, so here I am. I am trying to efficiently use prepared statements, and I don't just mean parameterizing a single statement, but compiling one for reuse many times. My question lies around the parameters and reuse and how to implement that correctly.
Generally I follow this procedure (contrived example):
SqlConnection db = new SqlConnection(...);
SqlCommand s = new SqlCommand("select * from foo where a=#a", db);
s.Parameters.Add("#a", SqlDbType.VarChar, 8);
s.Prepare();
...
s.Parameters["#a"] = "bozo";
s.Execute();
Super, that works. However, I don't want to do all of these steps (or the latter four) every time I run this query. That seems like it's counteracting the whole idea of prepared statements. In my mind I should only have to change the parameters and re-execute, but the question is how to do that?
I tried s.Parameters.Clear(), but this actually removes the parameters themselves, not just the values, so I would essentially need to re-Add the parameters and re-Prepare again, which would seem to break the whole point as well. No thanks.
At this point I am left with iterating through s.Parameters and setting them all to null or some other value. Is this correct? Unfortunately in my current project I have queries with ~15 parameters which need to be executed ~10,000 times per run. I can shunt this iteration off into a method but was wondering if there is a better way to do this (without stored procs).
My current workaround is an extension method, SqlParameterCollection.Nullify, that sets all the parameters to null, which is fine for my case. I just run this after an execute.
I found some virtually identical but (IMHO) unanswered questions:
Prepared statements and the built-in connection pool in .NET
SQLite/C# Connection Pooling and Prepared Statement Confusion (Serge was so close to answering!)
The best answer I could find is (1) common sense above and (2) this page:
http://msdn.microsoft.com/en-us/magazine/cc163799.aspx
When re-using a prepared SqlCommand, surely all you need to do is set the parameter values to the new ones? You don't need to clear them out after use.
For myself, I haven't seen a DBMS produced in the last 10 years which got any noticeable benefit from preparing a statement (I suppose if the DB Server was at the limits of its CPU it might, but this is not typical). Are you sure that Preparing is necessary?
Running the same command "~10,000 times per run" smells a bit to me, unless you're uploading from an external source. In that case, Bulk Loading might help? What is each run doing?
To add to Simon's answer, prior to Sql 2005 Command.Prepare() would have improved query plan caching of ad-hoc queries (SPROCs would generally be compiled). However, in more recent Sql Versions, provided that your query is parameterized, ad-hoc queries which are also parameterized can also be cached, reducing the need for Prepare().
Here is an example of retaining a SqlParameters collection changing just the value of those parameters values which vary, to prevent repeated creation of the Parameters (i.e. saving parameter object creation and collection):
using (var sqlConnection = new SqlConnection("connstring"))
{
sqlConnection.Open();
using (var sqlCommand = new SqlCommand
{
Connection = sqlConnection,
CommandText = "dbo.MyProc",
CommandType = CommandType.StoredProcedure,
})
{
// Once-off setup per connection
// This parameter doesn't vary so is set just once
sqlCommand.Parameters.Add("ConstantParam0", SqlDbType.Int).Value = 1234;
// These parameters are defined once but set multiple times
sqlCommand.Parameters.Add(new SqlParameter("VarParam1", SqlDbType.VarChar));
sqlCommand.Parameters.Add(new SqlParameter("VarParam2", SqlDbType.DateTime));
// Tight loop - performance critical
foreach(var item in itemsToExec)
{
// No need to set ConstantParam0
// Reuses variable parameters, by just mutating values
sqlParameters["VarParam1"].Value = item.Param1Value; // Or sqlParameters[1].Value
sqlParameters["VarParam2"].Value = item.Param2Date; // Or sqlParameters[2].Value
sqlCommand.ExecuteNonQuery();
}
}
}
Notes:
If you are inserting a large number of rows, and concurrency with other inhabitants of the database is important, and if an ACID transaction boundary is not important, you might consider batching and committing updates such that fewer than 5000 row locks are held on a table at a time, to guard against table lock escalation.
Depending on what work your proc is actually doing, there may be an opportunity to parallelize the loop, e.g. with TPL. Obviously connection and commands are not thread safe each Task will require its own connection and Reusable Command - the localInit overload of Parallel.ForEach is ideal for this.
Consider a block of code like this:
using (SqlConnection c = new SqlConnection("{connection string}"))
{
c.Open();
using (SqlCommand cmd = new SqlCommand("INSERT INTO Table (Field1, Field2) VALUES (#Field1, #Field2)", c))
{
cmd.Parameters.AddWithValue("#Field1", "some value");
cmd.Parameters.AddWithValue("#Field2", 10);
cmd.ExecuteNonQuery();
}
}
I would like to be able to see the actual statement sent to the server, but the kicker is I don't have access to SQL Server Profiler via our Network/SQL administrators. Is there any way I can get at the actual statement submitted to the server?
There are a range of tools that do exactly this. If you are in asp.net, you may find MiniProfiler a good start (you can use it on other platforms, but the UI tooling is stronger on asp.net). The main change involved here would be to move from SqlCommand etc to use c.CreateCommand, since it works by wrapping the connection (decorator pattern) - while it is still a DbConnection, the outermost object is not a SqlConnecion any more. I have to say, though - in this particular example you already know the exact command sent to the server. It is useful for finding the surprises, though. If that doesn't suit, "Hibernating Rhinos" offer a range of profiling tools, and many Orpheus general-purpose profiling tools will include ado.net tools; the main reason I mention MiniProfiler is that is is free, readily available, and very low impact (it is what we wrote to profile stackoverflow.com - and we leave it "on" 24x7)
I have a reports page where you can enter the query manually for a report. How can I block any INSERT, UPDATE or DELETE statements, and only run SELECT?
using (var connection = new SQLiteConnection(connectionString))
{
var da = new SQLiteDataAdapter
{
SelectCommand = new SQLiteCommand(query, connection)
};
try
{
da.Fill(table);
}
I could check if the query string contains "INSERT", "UPDATE" or "DELETE", but I don't think it's a good practice.
You could use an EXPLAIN statement to break the query down into VM instructions and examine the opcode column of the output. If the value "OpenWrite" occurs then the query is not read-only.
Checking the query string is not good practice? Compared to what? Allowing a user to enter any SQL statement they want to in your report page? I can't think of a much worse practice than that. If you're going to allow that sort of thing, you absolutely need to somehow restrict the types of statements they enter, and maybe require a Where clause (to avoid millions of rows being returned) etc.
in fact did you check what happens when you try to fill the table with the data adapter having anything else than a select in the query variable? I doubt you get an empty table or dataset, I would expect an exception in which case you could rollback the transaction.
I would anyway try to create the connection as readonly as suggested above by Sorax and I would actually parse the query variable as well.
Since the SQlite database is just one file, my guess is that you can make the database readonly through the filesystem. This is of course not a fancy solution but is one that does not require any code (of course except when you're throwing exceptions when writing isn't possible).
A) Use a read-only connection - I think that would be almost the best solution
B) Use more than one TextBox as Input (but this would become more a solution like checking the String for Insert etc.)
For Example
Select |____| From |_________| Where |_______|
Edit: to answer your comment just have a look at http://www.sqlite.org/c3ref/open.html especially the topic "SQLITE_OPEN_READONLY" - I haven't done anything with sqlite now, but I think that should do the trick...