How can I achieve something like following
SqlConnection connection = new SqlConnection("CNStr");
SqlCommand Command = connection.CreateCommand();
command.CommandText = "DECLARE #find varchar(30) = 'Test'; ";
command.ExecuteNonQuery();
command.CommandText = "select * from Some_View_Which_Has_Used_That_Variable_In_ItsWhere_Condition"
var result = command.ExecuteNonQuery();
command.Dispose();
connection.Dispose();
I want to declare some per connection variable, and then use them everywhere, in views, functions and so on.
How can I achieve this goal without using temp table and contextId ?
Thanks in advance
You can not use DECLARE in your sql statement.
It is not even valid Data Manipulation Language statement. It doesn't even effect your Some_View_Which_Has_Used_That_Variable_In_ItsWhere_Condition at all.
That's why it looks pointless to me. If you really want to declare #find, you can write in your view as manual.
And use using statement to dispose your SqlConnection and SqlCommand.
For example;
using(SqlConnection con = new SqlConnection(CNStr))
using(SqlCommand command = con.CreateCommand())
{
//
}
The only obvious per-connection object that persists for longer than a single batch (i.e. your ExecuteXXX calls) would be a temp table:
command.CommandText = "CREATE TABLE #Settings (find varchar(30) not null);
INSERT INTO #Settings (find) VALUES ('Test')";
command.ExecuteNonQuery();
command.CommandText = "select * from Some_View where
SomeColumn in (select find from #Settings)"
var result = command.ExecuteReader();
All variables (whether scalar or table-valued) have lifetimes that end at the end of their respective batches.
A few other things can be set at the connection/session level (e.g. CONTEXT_INFO) but they tend to have fixed, limited data types.
Related
Below is the line of code where I truncate table records. The table value is coming from the front end. In my Veracode scan, it is showing SQL injection. How can I avoid this? I cannot create a stored procedure as the connection string is dynamic where I need to truncate this table. Is there another approach?
SqlCommand cmd = connection.CreateCommand();
cmd.Transaction = transaction;
cmd.CommandText = "TRUNCATE TABLE " + tablename;
cmd.ExecuteNonQuery();
You need dynamic sql:
string sql = #"
DECLARE #SQL nvarchar(150);
SELECT #SQL = 'truncate table ' + quotename(table_name) + ';'
FROM information_schema.tables
WHERE table_name = #table;
EXEC(#SQL);";
using (var connection = new SqlConnection("connection string here"))
using (var cmd = new SqlCommand(sql, connection))
{
cmd.Transaction = transaction;
cmd.Parameters.Add("#table", SqlDbType.NVarChar, 128).Value = tablename;
connection.Open();
cmd.ExecuteNonQuery();
}
This is one of very few times dynamic SQL makes things more secure, rather than less. Even better, if you also maintain a special table in this database listing other tables users are allowed to truncate, and use that rather than information_schema to validate the name. The idea of letting users just truncate anything is kind of scary.
Parametrized or not, you can make it only a little more secured in this case. Never totally secured. For this you need
create table TruncMapping in DB where you store
id guid
statement varchar(300)
your data will look like
SOME-GUID-XXX-YYY, 'TRUNCATE TABLE TBL1'
In your front end use a listbox or combobox with text/value like "Customer Data"/"SOME-GUID-XXX-YYY"
In your code use ExecuteScalar to execute Select statement from TruncMapping where id = #1 , where id will be parameterized GUID from combo value
Execute your truncate command using ExecuteNonQuery as you do now but with a retrieved string from previous call.
Your scan tool will most likely choke. If it is still thinking code is unsafe, you can safely point this as false positive because what you execute is coming from your secured DB. Potential attacker has no way to sabotage your "non-tuncatable tables" because they are not listed in TruncMapping tables.
You've just created multi-layered defense against sql injection.
here is one way to hide it from scanning tools
private const string _sql = "VFJVTkNBVEUgVEFCTEU=";
. . . .
var temp = new { t = tablename };
cmd.CommandText =
Encoding.ASCII.GetString(Convert.FromBase64String(_sql)) + temp.t.PadLeft(temp.t.Length + 1);
security by obscurity
I'm trying to follow best practice (and also remove Visual Studio Code Analysis warnings) by using parameters when dropping a SQL Server index.
Not using parameters works fine:
string tableName = "dbo.TableName";
SqlCommand sqlCommand = new SqlCommand("DROP INDEX Blah ON " + tableName);
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
However, when I try to use a parameter I get an error
Incorrect syntax near '#TableName'.
Code:
string tableName = "dbo.TableName";
SqlCommand sqlCommand = new SqlCommand("DROP INDEX Blah ON #TableName");
sqlCommand.Parameters.Add(new SqlParameter("TableName", tableName));
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
What am I doing wrong?
You are doing nothing wrong. Parameters cannot be used to replace identifiers -- column names/aliases, table names/aliases, schema names, and database names. They also cannot be used to replace function names or operators or keywords.
That is a long list. They can be used to replace constants in the query.
I guess the way to remember this is that the parameterized query can be pre-compiled. In order to compile a query, all object references need to be resolved -- so the values cannot be provided by parameters.
You have already solved the problem by putting the table in the string. You can use quotename() to help protect against injection (see here).
DROP INDEX is a DDL statement, most DDL statements don't accept parameterized values. The best you can do is use dynamically constructed SQL and escape the table name using QUOTENAME
string tableName = "dbo.TableName";
string sql = #"
declare #sql nvarchar(500)
set #sql = N'DROP INDEX Blah ON ' + QUOTENAME(#TableName)
exec sp_executesql #sql
";
SqlCommand sqlCommand = new SqlCommand("");
sqlCommand.Parameters.Add("#TableName", SqlDbType.NVarChar, 50).Value = tableName;
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
I also updated your code to use the more "normal" way to add a parameter, explicitly setting the datatype of the parameter.
I was wondering if it is possible for the update button to save the changes made in the table. I wrote this code but I have no idea how it could possibly work
This is the code i wrote for the update button:
string conString = "Data Source=MIRANDA-PC;Initial Catalog=Futebol do Rosa;Integrated Security=True";
SqlConnection con = new SqlConnection(conString);
string selectSql = "Update Players$ set Player Name='" + dataGridView2.Text + "";
SqlCommand cmd = new SqlCommand(selectSql, con);
con.Open();
This is the table I want to update the values in:
Well, you just need to execute your query with ExecuteNonQuery.
But more important, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Also use using statement to dispose your SqlConnection and SqlCommand.
And if your table or column names more than one word, you need to use them with [] as [Player Name]. And honestly, it is a little bit weird to use $ sign in a table name.
using(SqlConnection con = new SqlConnection(conString))
using(SqlCommand cmd = con.CreateCommand())
{
cmd.CommandText = "Update Players$ set [Player Name] = #name";
cmd.Parameters.Add("#name", SqlDbType.NVarChar, 16).Value = dataGridView2.Text;
con.Open();
cmd.ExecuteNonQuery();
}
You have to execute your SQL query with your db object.
dbinstance.ExecuteSqlCommand(string sqlcommand, object[] params);
This method is both for DDL and DML.
you can also use ExecuteNonQuery method.
cmd.CommandText = "Update Players$ set [Player Name] = #Playername";
cmd.Parameters.Add("#Playername", SqlDbType.NVarChar, 16).Value = dataGridView2.Text;
con.Open();
cmd.ExecuteNonQuery();
The best solution (if possible) to to convert your DAL (Data Access Layer) to Entity-framework based, instead of writing your own SQL queries. This is safe-by-design and never is vulnerable to SQL Injection of any kind.
Here is some mockup code:
using (AppEntities currDb = new AppEntities)
{
Players PlayerToEdit =
from player in currDb.Players
where player.PlayerID == lngPlayerID
select player.First();
PlayerToEdit.PlayerName = dataGridView2.Text;
currDb.SaveChanges();
}
You can read about it some more here:
https://msdn.microsoft.com/en-us/data/ef.aspx
I'm new in C# programming, so I'll appreciate if anyone can help me. I know there are similar question but I still can't find the solution for my problem. I'm developing a mock system, where when user bought the product, the system will store all the transaction details. the problem is, I cannot insert the data into the database. Here's the code:
using (SqlConnection conn = new SqlConnection
(ConfigurationManager.ConnectionStrings["database"].ConnectionString))
{
string QueryA = "#Insert into TransDetails(AccountNumber,Amount,Provider"
+ ",Mobile Number,TransNum,TransDate, Status) "
+ " Values (#AccountNumber,#Amount,#Provider,#Mobile Number,"
+ "#TransNum,#TransDate,#Status";
using (SqlCommand cmd = new SqlCommand("InsertRecord", conn))
{
conn.Open();
cmd.CommandType = CommandType.Text;
cmd.CommandText = QueryA;
cmd.Parameters.AddWithValue("#AccountNumber", acc.Text);
cmd.Parameters.AddWithValue("#Amount", lblAmount.Text);
cmd.Parameters.AddWithValue("#Provider", lblProvider.Text);
cmd.Parameters.AddWithValue("#Mobile Number", lblNumber.Text);
cmd.Parameters.AddWithValue("#TransNum", lblTrans.Text);
cmd.Parameters.AddWithValue("#TransDate", lblDate.Text);
cmd.Parameters.AddWithValue("#Status", status.Text);
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch
{
lblMessage.Text = "Error";
}
finally
{
conn.Close();
}
}
}
and the stores procedures are as follows:
ALTER PROCEDURE InsertRecord1
#AccountNumber int,
#Amount nchar(10),
#Provider nchar(10),
#MobileNumber int,
#TransNum nchar(10),
#TransDate date,
#Status nchar(10)
AS
Insert into TransDetails(AccountNumber,Amount,Provider,MobileNumber,TransNum,TransDate,Status)
Values (#AccountNumber,#Amount,#Provider,#MobileNumber,#TransNum,#TransDate,#Status)
return
Really appreciate any help.
P/S: i dont know why the beginning of the stored procedures started with "alter".
I may be reading it wrong, but it looks like your stored procedure is not used at all. Try commenting out "cmd.CommandText = QueryA;" and substitute "cmd.CommandText = "InsertRecord1";" and change CommandType to StoredProcedure.
QueryA, by the way, is missing a paren at the end. However, the whole thing is unnecessary since you have a stored procedure that does the same thing and it's almost always preferable to use a stored procedure rather than embedded DML.
You must escape Mobile Number while brackets
Insert into TransDetails(AccountNumber,Amount,Provider,[Mobile Number],...
and remove the space in your parameter
...,#MobileNumber,#TransNum,#TransDate,#Status
and change the paramname in your command parameter
cmd.Parameters.AddWithValue("#MobileNumber", lblNumber.Text);
but seeing your stored procedure, the column Mobile Number has no space between it. Is it a typo error in your query on QueryA? If it is, then remove the space on it (also on parameter name)
Insert into TransDetails(AccountNumber,Amount,Provider,MobileNumber,...
or
change your CommandType.Text to CommandType.StoredProcedure and remove this line,
cmd.CommandText = QueryA;
You're using the wrong overload of the SqlCommand constructor. According to MSDN:
new SqlCommand(string, SqlConnection) Initializes a new instance of the SqlCommand class with the text of the query and a SqlConnection.
What you need to do is either set your CommandType for the sql command to CommandType.StoredProcedure and not use QueryA, or initialize the sql command with QueryA and not make use of your stored procedure.
As you can see there is # at the start of your SQL Statement.
Also you are not really using the Store Procedure.
You can Try this:
using (SqlConnection conn = new SqlConnection (ConfigurationManager.ConnectionStrings["database"].ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("InsertRecord1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#AccountNumber", acc.Text);
cmd.Parameters.AddWithValue("#Amount", lblAmount.Text);
cmd.Parameters.AddWithValue("#Provider", lblProvider.Text);
cmd.Parameters.AddWithValue("#Mobile Number", lblNumber.Text);
cmd.Parameters.AddWithValue("#TransNum", lblTrans.Text);
cmd.Parameters.AddWithValue("#TransDate", lblDate.Text);
cmd.Parameters.AddWithValue("#Status", status.Text);
try
{
cmd.ExecuteNonQuery();
}
catch
{
lblMessage.Text = "Error";
}
finally
{
conn.Close();
}
}
Tho I don't use SQL Commands, Adapters...etc. to access the data from the SQL Database. I prefer Microsoft Data Access ApplicationBlocks which is easy-to-use library provided by Microsoft to access data from SQL Server.
Download
You can download it here http://download.microsoft.com/download/VisualStudioNET/daabref/RTM/NT5/EN-US/DataAccessApplicationBlock.msi
Introduction
https://web.archive.org/web/20210304123854/https://www.4guysfromrolla.com/articles/062503-1.aspx
I'm writing a method to insert a Student into a local SQL database that contains a table with information about Students:
public void AddStudent(string name, string teachName, string pass)
{
string dbfile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\Logo.sdf";
SqlCeConnection connection = new SqlCeConnection("Data Source=" + dbfile + "; Password = 'dbpass2011!'");
connection.Open();
SqlCeTransaction transaction = connection.BeginTransaction();
SqlCeCommand command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "INSERT INTO Students VALUES ('#name', '#id', '#pass', '#tname')";
command.Parameters.Add("#name", name);
command.Parameters.Add("#id", this.ID);
command.Parameters.Add("#pass", MD5Encrypt.MD5(pass));
command.Parameters.Add("#tname", teachName);
command.Prepare();
command.ExecuteNonQuery();
transaction.Commit();
connection.Dispose();
connection.Close();
}
Whenever I use this, it never inserts the data to the table when I look at the contents of the Students table in the database. Originally I had this return an int so I could see how many rows it affected, which it always returned 1, so I know it's working.
I've looked for answers to this, and the answer to similar questions was that the person asking was looking at the wrong .sdf file. I've made sure that I'm looking at the right file.
Any feedback would be much appreciated!
command.CommandText = "INSERT INTO Students VALUES ('#name', '#id', '#pass', '#tname')";
You should remove the extra single quotes - this should be:
command.CommandText = "INSERT INTO Students VALUES (#name, #id, #pass, #tname)";
Also I am not sure why you open a transaction for a single insert - that is also not needed.
You don't need to put single quote to parametrized query, in case of parametrized query the whole data will be parsed as required,
command.CommandText = "INSERT INTO Students VALUES (#name, #id, #pass, #tname)";
Also, its better to set parameter type, size and value explicitly as below:
SqlCeParameter param = new SqlCeParameter("#name", SqlDbType.NVarChar, 100);
param.Value = name; // name is a variable that contain the data of name field
//param.Value = 'Jhon Smith'; //Directly value also can be used
Hope this would be helpful, thanks for your time.
There is most likely an exception being raised in your code; you need to add a try/catch handler and/or debug the application to figure out exactly what is happening.
However, there are at least two issues with your code:
The prepare statement requires the data types of the parameters. From the MSDN documentation:
Before you call Prepare, specify the data type of each parameter in the statement to be prepared. For each parameter that has a variable-length data type, you must set the Size property to the maximum size needed. Prepare returns an error if these conditions are not met.
You need to close the connection before disposing it (this won't affect the insert, however).