I have a function which will get the records from the database.
public List<Issue> Load_Issues()
{
SqlDataReader Sdr;
List<Issue> ObjList = new List<Issue>();
cmd.CommandText = "Get_All_Issue";
try
{
cmd.Connection = con;
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
Sdr = cmd.ExecuteReader();
while (Sdr.Read())
{
// here I pull out records from database..
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
}
return ObjList;
}
The function I am using to bind the Gridview is as follows
public void Bind_Issues()
{
gdIssues.DataSource = Bl.Load_Issues()();
gdIssues.DataBind();
}
My stored procedure doesn't take any arguments. While the page loads for the first time it is working fine and binding the records to the gridview.
We have option to edit the records also, so what happening is after updating records I need to again bind the records to gridview. So I am again using my Load_Issues function to do it. But this time it is throwing error
Get_All_Issues has no parameters and arguments were supplied
You're most probably re-using the cmd instance in multiple places and you don't clear the parameters associated with it, thus creating the exception you're seeing.
Easiest fix would be to not re-use cmd, but if for whatever reason it's better for you, just make sure you use Clear on parameters before you execute it.
cmd.Parameters.Clear();
Try not using global connections, commands etc: open and close them within the method
public List<Issue> Load_Issues() {
//TODO: Put actual connection string here
using (SqlConnection con = new SqlConnection("Connection String here")) {
con.Open();
// Put IDisposable into using
using (SqlCommand cmd = new SqlCommand()) {
cmd.Connection = con;
cmd.CommandText = "Get_All_Issue";
cmd.CommandType = CommandType.StoredProcedure;
List<Issue> ObjList = new List<Issue>();
// Put IDisposable into using
using (var Sdr = cmd.ExecuteReader()) {
while (Sdr.Read()) {
//TODO: Pull out records from database into ObjList
}
}
return ObjList;
}
}
}
Try these
exec 'stored_procedure_name'
go
or
alter proc stored_procedure_name
as
begin
--Block of Statements
end
go
or
create proc stored_procedure_name
as
begin
--Block of Statements
end
go
Where go keyword will solved your problem.
Related
I have my classes from other projects which have strings for SQL queries.
See my example
public class OtherClass
{
myQuery = "SELECT * FROM v_My_View WHERE code = '#code'";
// I am targeting a view, not a stored procedure
}
My question is, if I use this in my commandText and just replace the #code with a value, is this valid argument against SQL injection?
If it is vulnerable with SQL injection - what are the other options for it?
I tried to use the
CMD.Parameters.AddWithValue("#code", _obj.code)
but it ruined my query.
I am using parameters when accessing my stored procedure, but not when accessing my views.
This is my main:
public class Main
{
public DataTable myMethod()
{
try
{
DataTable myTable = new DataTable("MyDataTable");
using (SqlCommand CMD = new SqlCommand())
{
CMD.Connection = RBOSUtil.DBConnection();
CMD.CommandType = CommandType.Text;
// this is the part I used the string from other class
CMD.CommandText = OtherClas.myQuery.Replace("#code", _obj.code);
using (SqlDataAdapter DA = new SqlDataAdapter(CMD))
{
DA.Fill(myTable);
}
}
}
catch
{
throw;
}
finally
{
//close connection
}
return myTable;
}
}
use this
public class Main
{
public DataTable myMethod()
{
DataTable myTable = new DataTable("MyDataTable");
try
{
using (SqlCommand CMD = new SqlCommand())
{
CMD.Connection = RBOSUtil.DBConnection();
CMD.CommandType = CommandType.Text;
//this is the part I used the string from other class
CMD.CommandText = OtherClas.myQuery;
CMD.Parameters.Add("#code", SqlDbType.NVarChar).value = _obj.code;
using (SqlDataAdapter DA = new SqlDataAdapter(CMD))
{
DA.Fill(myTable);
}
}
}
catch
{
throw;
}
finally
{
//close connection
}
return myTable;
}
}
declare datatable before try
You don't need quotes around the parameter name in the SQL statement:
myQuery = "SELECT * FROM v_My_View WHERE code = #code";. Otherwise what you're doing looks fine to me. It should work.
EDIT: I got confused between the original question and Ravi's answer. Somehow I missed the separator between the question and the first answer. Anyway, to answer the original question, yes, using String.Replace to replace #code with a value is subject to SQL injection vulnerabilities.
You should use SQL parameters, like the code in Ravi's answer, but you'll also need to modify the query to remove the quotes around the parameter name.
I'm trying to write a method which should communicate with database, but I'm not sure if my approach is right.
public void dbWorkerLogin(int workerNumber) {
// Connection string stored in "conn"
if (!new SqlCommand("Some Command WHERE id=" +workernumber,conn).executeReader().HasRows)
{
new SqlCommand("exec STORED_PROCEDURE1 " + workerNumber, conn).ExecuteNonQuery();
new SqlCommand("exec STORED_PROCEDURE2 " + workerNumber, conn).ExecuteNonQuery();
}
else
{
new SqlCommand("exec STORED_PROCEDURE3 " + workerNumber,conn).ExecuteNonQuerry();
}
1) Is it ok to write it like this and start each SqlCommand with keyword new? Or should I do something like:
SqlCommand command = new SqlCommand(null, conn);
command = ...;
and then recycle the variable 'command' or this way?
using(SqlCommand cmd = new SqlCommand("COMMAND", conn);
2) Will my procedures work or should I use SqlCommand.Prepare() function that will covert my data into correct datatypes? eg. workerNumber is int, but in database it is stored as decimal.
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parametres.Add("#id", SqlDbType.Decimal).Value = workNumber;
cmd.Prepare();
cmd.ExecuteNonQuery();
}
Can you please somehow sum up what to use, what better not to? Unfortunately I can't test that first code because of limited access to DB so I'm not sure if it can be executed without errors or not.
Thank you for any help on this subject!
EDIT:
After a few hours I reach to this stage:
public int getWorkerNumber(string uniqueID)
{
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT number FROM worker WHERE workerID = #id",conn))
{
cmd.Parameters.Add("#id", SqlDbType.Decimal).Value = uniqueID;
using (SqlDataReader reader = cmd.ExecuteReader())
{
int answer;
while (reader.Read())
{
answer = (int)reader.GetDecimal(0);
}
return answer;
}
}
}
}
And this one:
public string dbLoginWorker(int workerNumber)
{
SqlCommand cmd;
SqlDataReader reader;
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
cmd = new SqlCommand("SELECT column FROM table WHERE id= #workernumber", conn);
cmd.Parameters.Add("#workernumber", SqlDbType.Decimal).Value = workerNumber;
reader = cmd.ExecuteReader();
if (!reader.HasRows)
{
cmd = new SqlCommand("STORED_PROCEDURE1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 1 executed";
else
{
cmd = new SqlCommand("STORED_PROCEDURE2", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("#INT", SqlDbType.SmallInt).Value = 1;
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 2 executed";
}
}
}
Both methods are functional (if I did no mistake in rewriting :) ). I'm not sure which of these methods (1st or 2nd) are better in terms of stability and if this approach is better and more ressistant to SQL Injection. Can someone comment on this subject? Thank you again for any help!
1) It is best to always use USING blocks when possible. This includes SqlConnection, SqlCommand, SqlReader and other objects that implement IDisposable. USING blocks automatically close and dispose of the objects, so you do not have to do so.
2) I believe that you are using the Prepare() method in the wrong place. Look at the following StackOverflow article for proper usage:
PrepareMethodInstructions.
3) in the dbLoginWorker() method, the first query is just used to determine if rows are found. Therefore, I suggest changing the SELECT command to SELECT TOP 1 column FROM table WHERE id= #workernumber so that the query is faster and more efficient.
4) I do not believe your commands are subject to SQL Injection attacks because they are fully parameterized. Good job on that one.
5) As a general thought, I suggest reading up on refactoring techniques. Your dbLoginWorker() method could be made more readable and maintainable, as well as self-documenting, if you created three additional methods, one for each SQL command, and named them something appropriate. You could also setup a method for creating a connection based on a connection name, and you would not have as much duplicate code. For example:
public static SqlConnection GetConnection(string connectionName)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionName].ConnectionString);
conn.Open();
return conn;
}
public string dbLoginWorker(int workerNumber)
{
using (conn = GetConnection("dbConnect"))
{
if (CanFindWorkerNumber(conn, workerNumber))
ExecuteProcedure1(conn);
else
ExecuteProcedure2(conn);
}
}
public bool CanFindWorkerNumber (SqlConnection conn, int workerNumber)
{
bool success = false;
using (SqlCommand cmd = new SqlCommand("SELECT TOP 1 column FROM table WHERE id= #workernumber", conn))
{
cmd.Parameters.Add("#workernumber", SqlDbType.Decimal);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
success = cmd.ExecuteScalar() != null;
}
return success;
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal);
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
cmd.Parameters[1].Value = "text";
cmd.ExecuteNonQuery();
}
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal);
cmd.Parameters.Add("#INT", SqlDbType.SmallInt).Value);
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0] = workerNumber;
cmd.Parameters[1] = 1;
cmd.Parameters[2] = "text";
cmd.ExecuteNonQuery();
}
}
You could actually do this in one SQL commend. Right now you are pulling back a result set only to see if it has rows or not, then executing different commands based on that. You should be able to do that in one command, disposing of it and the connection appropriately:
var sql =
#"
IF EXISTS(Some Command WHERE id=#workernumber)
BEGIN
exec STORED_PROCEDURE1 #workernumber;
exec STORED_PROCEDURE2 #workernumber;
END
ELSE
exec STORED_PROCEDURE3 #workernumber;
";
Note that you're not vulnerable to SQL injection because you're not dealing with strings, only integers.
I am having a problem with an Output parameter in C#/Oracle. I have isolated the code that I need to get working.
This is part of a much larger SQL statement, so do not worry too much if it doesn't make sense. In short I need to copy a row, give it a new ID and return that new ID. I tried using "RETURNING" which did not work. I see no reason why the code below should not work, but I'm getting an "ORA-01036: illegal variable name/number" error. Can anyone see what I'm doing wrong?
using (OracleConnection conn = new OracleConnection(connString))
{
// Open connection and create command.
conn.Open();
using (OracleCommand cmd = new OracleCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("outValue", OracleType.Int32).Direction = ParameterDirection.Output;
cmd.CommandText = "SELECT seq.nextval INTO :outValue FROM dual";
try
{
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
// This is just to see the exception when it fails.
}
}
}
The name of the parameter doesn't match.
cmd.Parameters.Add(":outValue", OracleType.Int32).Direction.......;
^
I have also seen this variation on the query syntax
"BEGIN SELECT seq.nextval INTO :outValue FROM dual END;"
You are using named parameters. Try setting:
cmd.BindByName = true;
Have you tried 'returning' keyword like this?
This code works for me.
using (OracleConnection conn = new OracleConnection(connString))
{
// Open connection and create command.
conn.Open();
using (OracleCommand cmd = new OracleCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("outValue", OracleType.Int32).Direction = ParameterDirection.Output;
cmd.CommandText = "insert into table (id, value) values (seq.nextval, 'value') returning id into :outValue";
cmd.ExecuteNonQuery();
}
}
What is the easiest and most SQL-like way to insert textbox values into a SQL Server table? I found several ways, and all of them are too complicated for this simple thing I want to do.
If LINQ is too foreign for you, you can still do things the old-fashioned way:
string statement = "INSERT INTO mytable(mycolumn) VALUES (#text)";
SqlCommand command = new SqlCommand(statement);
command.Parameters.AddWithValue("#text", myTextBox.Text);
try{
SqlConnection connection = new SqlConnection(myConnectionString);
connection.Open();
command.Connection = connection;
command.ExecuteNonQuery();
} catch {
//do exception handling stuff
}
Edit: Here's another version that uses using to ensure that messes are cleaned up:
string statement = "INSERT INTO mytable(mycolumn) VALUES (#text)";
using(SqlCommand command = new SqlCommand(statement))
using(SqlConnection connection = new SqlConnection(myConnectionString)) {
try{
command.Parameters.AddWithValue("#text", myTextBox.Text);
connection.Open();
command.Connection = connection;
command.ExecuteNonQuery();
} catch {
//do exception handling stuff
}
}
If you want to do something quickly, use LINQ to SQL. It will take care of your Data Access Layer & Business objects.
Just go to LINQ to SQL Classes on Visual Studio & map your SQL server and add any tables you want to it.
Then you can use the objects it creates in your code behind to update values from textboxes.
http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx
public string ConnectionString
{
get
{
//Reading connection string from web.config
return ConfigurationManager.ConnectionStrings["ConnectionName"].ConnectionString;
}
}
public bool InsertEmployee()
{
bool isSaved = false;
int numberOfRowsAffected = 0;
string query = #"INSERT INTO Employee(EmployeeName, EmailAddress)
VALUES (#EmployeeName, #EmailAddress);
SELECT ##IDENTITY AS RowEffected";
SqlCommand cmd = new SqlCommand();
cmd.CommandText = query;
cmd.CommandType = System.Data.CommandType.Text;
cmd.Parameters.Add(new SqlParameter("#EmployeeName", txtEmployeeName.Text));
cmd.Parameters.Add(new SqlParameter("#EmailAddress", txtEmailAddress.Text));
try
{
using (SqlConnection cn = new SqlConnection(ConnectionString))
{
cmd.Connection = cn;
cn.Open();
object result = cmd.ExecuteScalar();
isSaved = Convert.ToInt32(result) > 0 ? true : false;
}
}
catch (Exception ex)
{
isSaved = false;
}
return isSaved;
}
But, in multiple layer or multi-tier application you do need to create DTO(Data Transfer Object) to pass the data from layer to layer(or tier to tier)
Here's a simple way to do it. It looks complex because of the number of rows: Inserting Data in SQL Database
This is a really, really stupid question but I am so accustomed to using linq / other methods for connecting and querying a database that I never stopped to learn how to do it from the ground up.
Question: How do I establish a manual connection to a database and pass it a string param in C#? (yes, I know.. pure ignorance).
Thanks
using (SqlConnection conn = new SqlConnection(databaseConnectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "StoredProcedureName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#ID", fileID);
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}
}
One uses the SqlCommand class to execute commands (either stored procedures or sql) on SQL Server using ado.net. Tutorials abound.
Here's an example from http://www.csharp-station.com/Tutorials/AdoDotNet/Lesson07.aspx
public void RunStoredProcParams()
{
SqlConnection conn = null;
SqlDataReader rdr = null;
// typically obtained from user
// input, but we take a short cut
string custId = "FURIB";
Console.WriteLine("\nCustomer Order History:\n");
try
{
// create and open a connection object
conn = new
SqlConnection("Server=(local);DataBase=Northwind;Integrated Security=SSPI");
conn.Open();
// 1. create a command object identifying
// the stored procedure
SqlCommand cmd = new SqlCommand(
"CustOrderHist", conn);
// 2. set the command object so it knows
// to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// 3. add parameter to command, which
// will be passed to the stored procedure
cmd.Parameters.Add(
new SqlParameter("#CustomerID", custId));
// execute the command
rdr = cmd.ExecuteReader();
// iterate through results, printing each to console
while (rdr.Read())
{
Console.WriteLine(
"Product: {0,-35} Total: {1,2}",
rdr["ProductName"],
rdr["Total"]);
}
}
finally
{
if (conn != null)
{
conn.Close();
}
if (rdr != null)
{
rdr.Close();
}
}
}
3 things no one else has shown you yet:
"Stacking" using statements
Setting an explicit parameter type rather than letting .Net try to pick one for you
"var" keyword
.
string sql = "MyProcedureName";
using (var cn = new SqlConnection(databaseConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ParameterName", SqlDbType.VarChar, 50)
.Value = "MyParameterValue";
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}