Can somebody tell me the pros and cons of this code? I know I can use stored procedures instead, but would it be easy to SQL inject this code considering I had a textbox where admins could input the commentid?
string commentId = a.Text;
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ForumDatabaseConnectionString"].ConnectionString);
con.Open();
string sql = "DELETE FROM Comment WHERE Comment.commentId = #commentid";
SqlCommand cmd = new SqlCommand(sql, con);
cmd.Parameters.AddWithValue("#commentid", commentId);
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
Yes, it looks fine, since you're using paramterized sql. However, you haven't given your table an alias, so I thing your sql should be
DELETE FROM Comment WHERE commentId = #commentid
As well as protecting you from sql injection attacks, Sql Server will know that this sql may be called again with different parameters, so can cache an efficient execution plan for it.
As an aside, you should always dispose of connections after using them.
string commentId = a.Text;
using (SqlConnection con = new SqlConnection(ConfigurationManager
.ConnectionStrings["ForumDatabaseConnectionString"].ConnectionString))
{
con.Open();
string sql = "DELETE FROM Comment WHERE Comment.commentId = #commentid";
using(SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.Parameters.AddWithValue("#commentid", commentId);
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
}
As you can see, there is a fair amount of code for such a simple operation. You may wish to take a look at dapper, which will remove a lot of these issues. There are many libraries to help you, which are off-topic here, but its a lightweight, popular one
Pros:
Good thing is you are using parameters for command which is sql injection safe.
Cons:
Not well written.
Not using function for CRUD. Always Use functions to do CRUD operation.
No Use of Using block. Always use using block, so you don't need to dispose connection & command. You don't need to manually close it.
Use following code in DataAccessLayer.
public void DeleteComment(int commentId)
{
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ForumDatabaseConnectionString"].ConnectionString))
{
con.Open();
string sql = "DELETE FROM Comment WHERE Comment.commentId = #commentid";
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.Parameters.AddWithValue("#commentid", commentId);
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
}
}
You can write connection open code in separate function too.
Check this article for more detail:
https://www.codeproject.com/Articles/813965/Preventing-SQL-Injection-Attack-ASP-NET-Part-I
Related
Because SqlCommand implements IDisposable, I would normally approach an ADO query as follows.
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(query, connection))
{
// Execute command, etc. here
}
However, what if I need to execute multiple commands during a single connection? Do I really need a new using block for each command?
The examples I found from Microsoft don't use a using block for SqlCommands (or even call Dispose()). What are best practices regarding disposing SqlCommand?
Sure, best practice is to dispose them.
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command1 = new SqlCommand(query1, connection))
{
// Execute command, etc. here
}
using (SqlCommand command2 = new SqlCommand(query2, connection))
{
// Execute command, etc. here
}
using (SqlCommand command3 = new SqlCommand(query3, connection))
{
// Execute command, etc. here
}
}
MSDN probably doesn't show it because it's not really needed in case of SqlCommand. But in my opinion it's bad from microsoft to not use this pattern on every object that implements IDdisosable because people aren't getting used to it.
The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block.
In this case you use using for each command block:
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand cmd1= new SqlCommand(query1, connection))
{
}
using (SqlCommand cmd2= new SqlCommand(query2, connection))
{
}
}
No you do not. There are 2 ways that your multiple commands could be bundled within one connection and command.
The first would be to just reuse the existing CMD object, but changing the CommandText and other CMD.properties as needed.
using (SqlConnection con = new SqlConnection(connectionString)) {
con.Open();
using (SqlCommand cmd = new SqlCommand(query1, con)) {
// cmd.CommandType = CommandType.xxxxx
// add any parameters
// Execute()
cmd.CommandText = query2;
// reset CommandType if needed
// adjust parameters if needed
// Execute()
cmd.CommandText = query 3;
// reset CommandType if needed
// adjust parameters if needed
// Execute()
}
con.Close();
}
The second method would be to create a Stored Procedure on the database server and call that in the one CMD object
-- Database
CREATE PROCEDURE schema.sproc_CommandBatch (
-- any variables here
) AS
BEGIN
-- query 1
-- query 2
-- query 3
END
GO
// C#
using (SqlConnection con = new SqlConnection(connectionString)) {
con.Open();
using (SqlCommand cmd = new SqlCommand("schema.sproc_CommandBatch", con)) {
// cmd.CommandType = CommandType.StoredProcedure
// add any parameters
// Execute()
}
con.Close();
}
Best practice is that if it implements IDisposable then Dispose() it. In fact the examples here do all call Dispose. With a wrinkle:
The first example, where it opens a SqlDataReader, calls .Close() on the command in the finally clause. In fact, .Close is just a wrapper around .Dispose() (and no longer appears in the documentation). Jonathan Wood in his comment pointed out that the Close is on the reader. This is wrong.
For multiple calls on the same connection you can:
Re-use the one command (declared in one using). I don't like this
myself, but that's just me: I find it...ugly.
Use multiple commands in multiple usings. That's what I'd do.
'Best Practice' is a bit amorphous. There's never unanimous agreement that what person A touts as Best Practice is better than what person B is recommending, let alone C, D or E. Use your own judgement.
Every time I try to run my code, I get this exception:
An unhandled exception of type 'System.Data.SqlClient.SqlException'
occurred in System.Data.dll
Additional information: Incorrect syntax near ')'.
Tried multiple workarounds, but I never get past the ExectueNonQuery line. Can someone tell me what's wrong with it?
private void button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(#"Data Source=CHARLIE-PC\MSSQLSERVER1;Initial Catalog=Tema;Integrated Security=True;");
con.Open();
SqlCommand cmd = new SqlCommand("INSERT INTO Fisier (idFisier, Nume, idFolder) VALUES ('"+idFis.Text+ "','"+ numeFis.Text + "','" +idFoldFis.Text +"',)", con);
cmd.ExecuteNonQuery();
con.Close();
}
While the other questions state the root problem, your trailing comma, you really must do better about your queries. Do not glue your query together like that, use parameters instead. If you do not you are opening yourself to huge security problems. Also you really must put the connection in a using statement so when a error does happen the connection will still be closed.
private void button1_Click(object sender, EventArgs e)
{
using(SqlConnection con = new SqlConnection(#"Data Source=CHARLIE-PC\MSSQLSERVER1;Initial Catalog=Tema;Integrated Security=True;"))
{
con.Open();
SqlCommand cmd = new SqlCommand("INSERT INTO Fisier (idFisier, Nume, idFolder) VALUES (#idFis,#numeFis,#idFoldFis)",con);
cmd.Parameters.Add("#idFis", SqlDbType.NVarChar, -1).Value = idFis.Text;
cmd.Parameters.Add("#numeFis", SqlDbType.NVarChar, -1).Value = numeFis.Text;
cmd.Parameters.Add("#idFoldFis", SqlDbType.NVarChar, -1).Value = idFoldFis.Text;
cmd.ExecuteNonQuery();
}
}
The comma is your problem, but I would recommend a few other changes at least before moving on:
Don't embed your connection strings into each db connection request. Use app.config/web.config or anything else :)
Ensure your connections are commands are properly disposed of
Parameterize any SQL queries to prevent injection attacks
Abstract database commands into separate business layer
1. Utilize an "app.config" for connection strings
There are many docs out there on keeping a connection string secure, but at a minimum, don't embed straight into each of your connection code.
Add an "app.config" to your client project (or utilize the web.config of web projects). At a minimum, this looks like this:
<configuration>
<appSettings>
<add key="db" value="Data Source=CHARLIE-PC\MSSQLSERVER1;Initial Catalog=Tema;Integrated Security=True;" />
</appSettings>
</configuration>
Then add a reference to "System.Configuration" to your project, and you can reference it like this in your code:
var con = new SqlConnection(ConfigurationManager.AppSettings["db"]);
2. Ensure your connections ard commands are properly disposed
Wrap connections and commands in using. Here is an example:
using (var con = new SqlConnection(ConfigurationManager.AppSettings["db"]))
{
con.Open();
var sql = "/* My command here */";
using (var cmd = new SqlCommand(sql, con))
{
// SQL execution here
}
} // Closing is now handled for you (even if errors occur)
3. Parameterize any SQL queries to prevent injection attacks
Concatenating strings are very dangerous for SQL commands (just google "SQL Injection"). This is how to protect yourself.
using (var con = new SqlConnection(ConfigurationManager.AppSettings["db"]))
{
con.Open();
var sql = "INSERT INTO Fisier (idFisier, Nume, idFolder) VALUES (#idFisier, #nume, #idFolder)";
using (var cmd = new SqlCommand(sql, con))
{
cmd.Parameters.Add("#idFisier", SqlDbType.VarChar).Value = idFis.Text;
cmd.Parameters.Add("#nume", SqlDbType.VarChar).Value = numeFis.Text;
cmd.Parameters.Add("#idFolder", SqlDbType.VarChar).Value = idFoldFis.Text;
cmd.ExecuteNonQuery();
}
} // Closing is now handled for you (even if errors occur)
4. Abstract database commands into separate business layer
It is usually best practice and will save you many headaches by writing separate classes (even class library) as your business layer that only contain your data commands. Then your UI would only handle calling the business layer methods.
If your database ever changes or you need to do similar functionality in other parts of your UI, it won't be very fun updating the same query all over your UI as opposed to just updating a single spot in your business layer.
Make SQL being readable and parametrized and you'll find the routine easy to implement:
// Extract a method (or even a class): do not mix UI and business logic/storage
// Just RDBMS logic: no UI controls or something at all
private static void CoreInsertFisier(string idFisier, nume, idFolder) {
// Do not hardcode the connection string, but read it (from settings)
// wrap IDisposable into using
using (SqlConnection con = new SqlConnection(ConnectionStringHere)) {
con.Open();
// Make sql readable (use verbatim strings #"...")
// Make sql parameterized
string sql =
#"INSERT INTO Fisier (
idFisier,
Nume,
idFolder)
VALUES (
#prm_idFisier,
#prm_Nume,
#prm_idFolder)";
// wrap IDisposable into using
using (SqlCommand cmd = new SqlCommand(sql, con)) {
// Parameters.Add(...) is a better choice, but you have to know fields' types
cmd.Parameters.AddWithValue("#prm_idFisier", idFisier);
cmd.Parameters.AddWithValue("#prm_Nume", nume);
cmd.Parameters.AddWithValue("#prm_idFolder", idFolder);
cmd.ExecuteNonQuery();
}
}
}
...
private void button1_Click(object sender, EventArgs e) {
// UI: just one call - please insert these three textbox into db
CoreInsertFisier(idFis.Text, numeFis.Text, idFoldFis.Text);
}
You have an extra trailing comma:
private void button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(#"Data Source=CHARLIE-PC\MSSQLSERVER1;Initial Catalog=Tema;Integrated Security=True;");
con.Open();
SqlCommand cmd = new SqlCommand("INSERT INTO Fisier (idFisier, Nume, idFolder) VALUES ('"+idFis.Text+ "','"+ numeFis.Text + "','" +idFoldFis.Text +"')",con);
cmd.ExecuteNonQuery();
con.Close();
}
Anyway as others said, it is a very bad idea to concatenate your query that way, since it could lead you to have sql injection on your code.
Try removing the , before the closing )
SqlCommand cmd = new SqlCommand("INSERT INTO Fisier (idFisier, Nume, idFolder) VALUES ('"+idFis.Text+ "','"+ numeFis.Text + "','" +idFoldFis.Text +"')",con);
i want to delete data in my database and using this code but its now working
private static void DeletePreviousRecord()
{
string connectionString = "Data Source=ABDULLAH\\ABDULLAHZAFAR;Initial Catalog=FoodHunt;Integrated Security=True";
using (SqlConnection con = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("Delete From RestaurantsMenu", con))
{
try
{
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
var result = cmd.ExecuteNonQuery();
con.Close();
}
catch (Exception ex)
{ }
}
}
}
i tried this but this is not working, how can i do that, any suggestion?
Setting the CommandType to StoredProcedure when you clearly use a sql text directly cannot do any good at your code.
Remove that line because the default is CommandType.Text (and this is correct for your command)
But as stated in the comment above.
If you catch the exception, at least write in some log or display at
video what the error message is
If you don't add a WHERE clause at your sql statement, you delete
everything in the table (Probably you are lucky that this code has
not worked)
Looking at your comment below, if you want to delete every record (and reset the Identity column if any) a faster approach is
using (SqlCommand cmd = new SqlCommand("TRUNCATE TABLE RestaurantsMenu", con))
For a quick reading about the difference between TRUNCATE and DELETE look at this article
I'm quite used to using c# with SQL server. I have no idea why a simple statement would fail to insert data. My code is as follows:
query = "INSERT INTO MCDPhoneNumber ([MCDID],[PhoneNumber])" +
"VALUES("+maxid+", '"+tel+"')";
SqlConnection conn = new SqlConnection("Data Source=source; ...");
SqlCommand newCommand = new SqlCommand(query, conn);
int success= myCommand.ExecuteNonQuery();
if (success!= 1)
{
MessageBox.Show("It didn't insert anything:" + query);
}
First of all let me tell that I know that I should use parameters for data and I initially did, but when it failed I tried a simple query and it still fails. For addition I can tell that I have a similar insert just before that one in another table and it works. What's funnier is that when I copy paste query to SQL Server Management Studio it works. It also doesn't report any error in process.
====================== Edit ===============================
If you wish to use old command object (i.e. myCommand) then use following code instead of creating a new command(newCommand)
myCommand.CommandText = query;
myCommand.CommandType = System.Data.CommandType.Text;
And then execute it
you are binding query with newCommand and executing myCommand.
====================== Edit ===============================
SqlCommand newCommand = new SqlCommand(query, conn);
here you have defined newCommand for SQLCOMMAND object
int success= myCommand.ExecuteNonQuery();
and you are accessing it as myCommand
And moreover i think you are not opening connection
First of all, you define your command as newCommand but you executing your myCommand.
You should always use parameterized queries for your sql queries. This kind of string concatenations are open for SQL Injection attacks.
query = "INSERT INTO MCDPhoneNumber (MCDID, PhoneNumber) VALUES(#maxid, #tel)";
using(SqlConnection conn = new SqlConnection("Data Source=source; Initial Catalog=base; Integrated Security = true"))
{
SqlCommand newCommand = new SqlCommand(query, conn);
conn.Open();
newCommand.Parameters.AddWithValue("#maxid", maxid);
newCommand.Parameters.AddWithValue("#tel", tel);
int success= newCommand.ExecuteNonQuery();
if (success != 1)
{
MessageBox.Show("It didn't insert shit:" + query);
}
}
And please be more polite about your error messages :)
I have an application that allows my user to run queries against a database of their choice. The database can be either SQL server or Oracle. this method accepts two parameters from another class, first parameter is the connection string to the database the user chooses, and the second is the database type. that part works fine. what I am trying to do is cut back on the code I need to write and not type the query and connection stuff over and over. so, I would like to do something like this. Obviously this wont work, but I'm open to most solutions.
public void createTable(string connectstring, string rdbms)
{
if (rdbms == "oracle")
{
con = new OracleConnection(connectionString);
con.Open();
OracleCommand query = con.CreateCommand();
}
else if (rdbms == "SQL Server")
{
con = new SqlConnection(connectionString);
con.Open();
SqlCommand query = con.CreateCommand();
}
else
{
// broke
}
query.CommandText = "CREATE TABLE " + RndName +
" (Col0 Varchar(10),Col1 Varchar(10), Col2 Varchar(10))";
query.ExecuteNonQuery();
con.Close();
executeInsertTransactions(connectstring);
}
This problem is generally solved via interfaces. There may be these common interfaces:
IConnection
IDataProvider
IRepository
Implement interfaces using MySql database, such as class MySqlConnection : IConnection. For Oracle, add class MsOracleConnection : IConnection.
Ideally you should abstract all the functionality into common interfaces. You will have to provide implementations for each database/storage engine you want to support. At runtime, you will use IoC container and DI principle to set up the current implementation. All the child dependencies will use interfaces passed in as parameters to constructor (or properties or methods)
You can create more abstract code by leveraging the framework's DbProviderFactory and using the obtained Db* classes.
Dim con As System.Data.IDbConnection
Dim cmd As System.Data.IDbCommand
Select Case ConDBType
Case TypeDatabase.SqlServer
con = New OleDbConnection(CN.ConnectionString)
cmd = New OleDbCommand
Case TypeDatabase.MySql
con = New MySqlConnection(CNMySql.ConnectionString)
cmd = New MySqlCommand
Case TypeDatabase.Access
Call InitNameing()
ConDBAccess.DataSource = PreparToRootNameing() & "\T" & NAME_SYSTEMDB
con = New OleDbConnection(CN.ConnectionString)
cmd = New OleDbCommand
End Select
cmd.Connection = con
con.Open()
cmd.CommandText = SQLUpdate
cmd.ExecuteNonQuery()