I have the below block of code:
private TData ExecuteReturnData<TData>(string procName, Func<IDataReader, TData> translator, SqlConnection sqlCon, params SqlParameter[] parameters)
{
using var sqlCmd = CreateCommand(procName, sqlCon, parameters);
sqlCmd.CommandTimeout=120;
using var reader = sqlCmd.ExecuteReader();
var elems = translator(reader);
return elems
}
Here I want to see the complete SQL command execution with params in SQL Server code block. How can I check that here?
What you're asking for does not exist. It never exists at any point. The entire purpose of using parameterized queries is the parameter data is NEVER substituted directly into the SQL command string, and therefore will not be available to view in that way.
Parameterized queries are more than simply sanitizing or escaping in the parameter data in the proper way; they quarantine the data from the command, so the two can never meet.
That is, if you have this query:
SELECT * FROM Users WHERE FirstName= #FirstName
and this parameter value:
Samuel
instead of something like this:
SELECT * FROM Users WHERE FirstName = 'Samuel'
The parameter data is sent to the server in a completely separate block than the SQL command. The server receives both parts and does something more like this:
DECLARE #FirstName nvarchar(40) = LoadParameterFromClient()
SELECT * FROM Users WHERE FirstName= #FirstName
(Note: the actual mechanism for this is sp_executesql)
But for what it's worth, I tend to structure similar C# code more like this:
private IEnumerable<TData> ExecuteReturnData<TData>(string SQL, Func<IDataRecord, TData> translator, Action<SqlParameterCollection> addParams)
{
using var conn = new SqlConnection(" ... "); // My data layer knows about the database I'm using, so I don't need to pass in a conneciton
using var cmd = new SqlCommand(SQL, conn);
if (addParams is object) addParams(cmd.Parameters);
conn.Open();
using var reader = sqlCmd.ExecuteReader();
while (reader.Read())
{
yield return translator(reader);
}
}
Then I'd call it using a similar example as above like this:
var results = ExecuteReturnData<string>("SELECT FirstName, LastName FROM Users WHERE FirstName=#FirstName",
p => p.Add("#FirstName", SqlDbType.NVarchar,40).Value = "Samuel",
r => r["LastName"] + ", " + r["FirstName"]);
For more complex result types I'd have a static FromSQL(IDataRecord data) method on the target type, to avoid making this function call too difficult to read:
var results = ExecuteReturnData<User>("SELECT * FROM Users WHERE FirstName=#FirstName",
p => p.Add("#FirstName", SqlDbType.NVarchar,40).Value = "Samuel",
User.FromSQL);
As a project grows I might also collect these methods into a separate static type, to avoid over-coupling between the data layer and client code.
And of course you can run stored procedures the same way:
var results = ExecuteReturnData("exec MyProcedure #Param1, #Param2" ... );
How to see SQL command execution with Stored Procedure
I want to see the complete SQL command execution with params in SQL Server code block. How can I check that here?
In Visual Studio open the SQL Server Object Explorer > New Connection to SQL Server > Expand Database > Expand Programmatibility > Stored Procedures and Right Click on a Sproc and choose Debug Procedure. You can step though the T-SQL Code. But you can't jump into the T-SQL from the .Net Code which sounds like what you want.
Debug Procedure > Press F11 to Step into the Stored Procedure:
REF: https://learn.microsoft.com/en-us/sql/ssms/scripting/transact-sql-debugger?view=sql-server-ver16
Related
I am working on a project where the client has reported an SQL injection flaw in the code. Here is my codeā¦
1 public int ExecuteNonQuery(string query, SqlParameter[] parameters)
2 {
3 using (SqlCommand command = CreateCommand(query, parameters))
4 {
5 int rowsAffected = command.ExecuteNonQuery();
6 return rowsAffected;
7 }
8 }
And the CreateCommand method goes as
private SqlCommand CreateCommand(string commandText, SqlParameter[] parameters)
{
SqlCommand retVal = this.connection.CreateCommand();
retVal.CommandText = commandText;
retVal.CommandTimeout = this.commandsTimeout;
retVal.Parameters.AddRange(parameters);
return retVal;
}
The flaw is reported at line number 3. I am unable to understand what kind of attack an happen here as this is a console application. But I have to fix the flaw and I don't know how to fix it.
Query is
#"delete from {0} where runId in
( select runId from {0}
inner join
( select sId as sId_last,
wfId as wfId_last,
max(runId) as runId_last from {0} where endTime is NULL
group by sId, wfId ) t1
on endTime is NULL and sId = sId_last and wfId = wfId_last
and (runId <> runId_last or startTime < #aDateTime)
)";
Help appreciated.
Thanks.
that code is injection-free... But note that the methods that call ExecuteNonQuery could build the query by composing strings.
An injection attack happens when you do something like:
string name = ...; // A name selected by the user.
string query = "SELECT * FROM MyTable WHERE Name = '" + name + "'";
so when you compose a query using pieces of text that are of external origin.
Note that a more subtle injection attack could be multi-level:
string name = // The result of a query to the db that retrieves some data
// sadly this data has been manipulated by the attacker
string query = "SELECT * FROM MyTable WHERE Name = '" + name + "'";
In general you don't need a user interface to cause an injection attack...
You could query something from a web site/from the db, and use the unsanitized result to query the db (as in the last example), causing an injection attack... Or even using the content of the configuration file could cause an injection attack: the priviledges needed to modify the configuration file could be different than the ones needed to do something on the DB, and a malicious user could have the priviledges to modify the configuration file but not have direct access to the DB. So he could use the program as a trojan horse against the DB.
about the query
The weak point of that query (that is a composition of strings) is in how the {0} is calculated. Is it a string chosen in a group of fixed strings? Something like:
string tableName;
if (foo)
tableName = "Foo";
else if (bar)
tableName = "Bar";
or is it something more user controlled?
If the table names are fixed in code, then there shouldn't be any injection attack possible. If the table names are "extracted" from some user input/some other table the user could have access, we return to the problem I showed before.
You've exposed a public method which can be accessed by any code that allows any SQL expression to be executed.
I would look at changing that method to being internal or private instead so that not just any code can call that method.
Line 3:
using (SqlCommand command = CreateCommand(query, parameters))
Both Query and parameters are available in this line.
SQL injection should not be prevented by trying to validate your input; instead, that input should be properly escaped before being passed to the database.
How to escape input totally depends on what technology you are using to interface with the database.
Use prepared statements and parameterized queries. These are SQL
statements that are sent to and parsed by the database server
separately from any parameters. This way it is impossible for an
attacker to inject malicious SQL.
Lesson on SQL injection for your reference.link2
I'm trying to create something similar to SQL Server Management Studio.
I want the ability to run several queries on the same connection like one does in the same tab in SSMS, however from c# each SqlCommand is executed separately even with the same SqlConnection so they can't see each others DECLARE's
Example: If you run two queries:
DECLARE #ted varchar(100) = 'Ted A';
SELECT #ted as [Query1];
and the other
SELECT #ted as [Query2];
If you run these two, in order, separately in a SSMS tab, you should get two correct results, one from each query.
However the problem I have is if I have one SqlConnection and I create two SqlCommand objects then they don't see the #ted variable even though they are the same connection.
How can I create a connection to a sql server database so that it behaves like an SSMS tab in that each subsequent query is in the same scope, and therefore I can use #variables in all queries?
Note: the same behavior as in SSMS can also be found in the SQLCMD utility.
Edit: i have just realised what i have asked is not how SSMS works, this is actually impossible.
you should be able to access #tables etc from the same connection but not #variables
You should think of a single query window in SSMS as roughly equivalent to a single SQLCommand, and a single instance of SSMS as roughly equivalent to a single SqlConnection. TSQL Variables have batch scope not connection scope.
A SQLCommand executes a batch of TSQL, possibly including multiple executable statements. You can simple put both of your queries into a single command.
Now, if you want your command to return multiple values from multiple select statements, you would use the NextResult method of the SqlDataReader. This will move to the result set of the next statement. Process the results of the each statement, then use NextResult to interate through the result sets.
Simple example
queryText = "Declare #Ted Varchar(100) = 'Ted A'; SELECT #ted --Or some other query using #ted; Select #ted --or yet another query using #ted;"
command = new SqlCommand(queryText);
reader = command.ExecuteReader();
do
{
//Result set handling code goes here
do
{
//Record level handling code goes here
} while reader.Read();
//More result set handling code goes here
} while reader.NextResult();
Hope this helps
The DECLARE is scoped to a command and not a connection. If you want to reuse declares then you will need to utilize a T-SQL parser.
Otherwise you can get started with something like this:
var commandText =
"DECLARE #ted varchar(100) = 'Ted A';" +
"SELECT #ted as [Query1];" +
"SELECT #ted as [Query2];";
using(var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = new SqlCommand(commandText, connection))
{
using(var reader = command.ExecuteReader())
{
// There are two result sets and each result set has one result.
do
{
// You will need to use the Schema Table to dynamically
// generate the results view
var schema = reader.GetSchemaTable();
// "ColumnName" column will have the "Query1" and "Query2"
var columnNameColumn = schema.Columns["ColumnName"];
var row = schema.Rows[0][columnNameColumn];
Console.WriteLine(row);
// Now we write the results
while(reader.Read())
{
Console.WriteLine(reader.GetString(0));
}
}
while(reader.NextResult());
}
}
}
Recent bug report states that a method being called is crashing the service causing it to restart. After troubleshooting, the cause was found to be an obnoxious Oracle SQL call with thousands of strings passed. There is a collection of strings being passed to a method from an external service which often is more than 10,000 records. The original code used a where clause on the passed collection using the LIKE keyword, which I think is really, really bad.
public IList<ContainerState> GetContainerStates(IList<string> containerNumbers)
{
string sql =
String.Format(#"Select CTNR_NO, CNTR_STATE FROM CONTAINERS WHERE CTRN_SEQ = 0 AND ({0})",
string.Join("OR", containerNumbers
.Select(item => string.Concat(" cntr_no LIKE '", item.SliceLeft(10), "%' ")))
);
return DataBase.SelectQuery(sql, MapRecordToContainerState, new { }).ToList();
}
Clarification of in house methods used which may be confusing:
DataBase.SelectQuery is an internal library method using generics which gets passed the sql string, a function to map the records to .NET objects, and the parameters being passed and returns an IEnumerable of Objects of type retuned by the Mapping function.
SliceLeft is an extension method from another internal helper library that just returns the first part of a string up to the number of characters specified by the parameter.
The reason that the LIKE statement was apparently used, is that the strings being passed and the strings in the database only are guaranteed to match the first 10 characters. Example ("XXXX000000-1" in the strings being passed should match a database record like "XXXX000000-8").
I believed that the IN clause using the SUBSTR would be more efficent than using multiple LIKE clauses and replaced the code with:
public IList<ContainerRecord> GetContainerStates(IList<string> containerNumbers)
{
string sql =
String.Format(#"Select CTNR_NO, CNTR_STATE FROM CONTAINERS WHERE CTRN_SEQ = 0 AND ({0})",
string.Format("SUBSTR(CNTR_NO, 1, 10) IN ({0}) ",
string.Join(",", containerNumbers.Select(item => string.Format("\'{0}\'", item.SliceLeft(10) ) ) )
)
);
return DataBase.SelectQuery(sql, MapRecordToContainerState, new { }).ToList();
}
This helped slightly, and there were fewer issues in my tests, but when there are huge amounts of records passed, there is still an exception thrown and core dumps occur, as the SQL is longer than the server can parse during these times. The DBA suggests saving all the strings being passed to a temporary table, and then joining against that temp table.
Given that advice, I changed the function to:
public IList<ContainerRecord> GetContainerStates(IList<string> containerNumbers)
{
string sql =
#"
CREATE TABLE T1(cntr_num VARCHAR2(10));
DECLARE GLOBAL TEMPORARY TABLE SESSION.T1 NOT LOGGED;
INSERT INTO SESSION.T1 VALUES (:containerNumbers);
SELECT
DISTINCT cntr_no,
'_IT' cntr_state
FROM
tb_master
WHERE
cntr_seq = 0
AND cntr_state IN ({0})
AND adjustment <> :adjustment
AND SUBSTR(CTNR_NO, 1, 10) IN (SELECT CNTR_NUM FROM SESSION.T1);
";
var parameters = new
{
#containerNumbers = containerNumbers.Select( item => item.SliceLeft(10)).ToList()
};
return DataBase.SelectQuery(sql, MapRecordToContainerState, parameters).ToList();
}
Now I'm getting a "ORA-00900: invalid SQL statement". This is really frustrating, how can I properly write a SQL Statement that will put this list of strings into a temporary table and then use it in a SELECT Statement to return the list I need?
There are couple possible places could cause this error, it seams that the "DECLARE GLOBAL TEMPORARY" is a JAVA API, I don't think .net has this function. Please try "Create global temporary table" instead. And, I don't know whether your internal API could handle multiple SQLs in one select sql. As far as I know, ODP.net Command class can only execute one sql per call. Moreover, "create table" is a DDL, it therefore has its own transaction. I can't see any reason we should put them in the same sql to execute. Following is a sample code for ODP.net,
using (OracleConnection conn = new OracleConnection(BD_CONN_STRING))
{
conn.Open();
using (OracleCommand cmd = new OracleCommand("create global temporary table t1(id number(9))", conn))
{
// actually this should execute once only
cmd.ExecuteNonQuery();
}
using (OracleCommand cmd = new OracleCommand("insert into t1 values (1)", conn)) {
cmd.ExecuteNonQuery();
}
// customer table is a permenant table
using (OracleCommand cmd = new OracleCommand("select c.id from customer c, t1 tmp1 where c.id=tmp1.id", conn)) {
cmd.ExecuteNonQuery();
}
}
I have lots of SQL queries written inline in C# using .net framework.
For Example
string sMysql = #"
SELECT
[Something]
from Person where person_uid=12"
I want it to convert it to stored procedures which will be written in transact SQL.
Something like this:
CREATE PROCEDURE [dbo].[aspnet_AnyDataInTables]
#uid int
AS
BEGIN
SELECT
[Something]
from Person where person_uid=#uid
END
I can do it manually but I have lots of inline queries to convert. Is there a way to do this hectic job programmatically?
For your specific example, throw out the stored procedure idea, and change your code snippet to the following:
// initialize UID value and SQL query with parameter placeholder
int uid = 12;
sql = "SELECT [Something] FROM [Person] WHERE [person_uid] = #UID";
// initialize connection and open
using (SqlConnection connection = new SqlConnection("<connection string>")
{
SqlCommand command = new SqlCommand(sql, connection)
// add UID parameter
command.Parameters.Add(new SqlParameter("UID", uid);
try
{
connection.Open();
// execute and read results
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
// process results
}
}
catch (Exception ex)
{
// handle exceptions
}
}
As stated in question comments, stored procedures aren't necessarily faster than inline parameterized queries. SQL Server will even cache execution plans for queries that aren't parameterized, but I prefer the explicit declaration of parameters.
Take a look at this article on execution plan caching and reuse for SQL Server 2008 if you want more information.
I am working on a new project that needs to use Linq To SQL. I have been asked to create a generic or reusable Linq to SQL class that can be used to execute stored procedures.
In ADO.Net I knew how to do this by just passing in a string of what I wanted to execute and I could pass in different strings for each query I need to run:
SqlCommand cmd = new SqlCommand("myStoredProc", conn); // etc, etc
I am struggling with how to create something similar in Linq To SQL, if it is even possible. I have created a .dbml file and added my stored procedure to it. As a result, I can return the results using the code below:
public List<myResultsStoreProc> GetData(string connectName)
{
MyDataContext db = new MyDataContext (GetConnectionString(connectName));
var query = db.myResultsStoreProc();
return query.ToList();
}
The code works but they want me to create one method that will return whatever stored procedure I tell it to run. I have searched online and talked to colleagues about this and have been unsuccessful in finding a way to create reusable stored proc class.
So is there a way to create a reusable Linq to SQL class to execute stored procs?
Edit:
What I am looking for is if there is a way to do something like the following?
public List<string> GetData(string connectName, string procedureName)
{
MyDataContext db = new MyDataContext (GetConnectionString(connectName));
var query = db.procedureName();
return query.ToList();
}
I have reviewed the MSDN docs on Linq To Sql and these are showing the table in the IEnumerable:
IEnumerable<Customer> results = db.ExecuteQuery<Customer>(
#"select c1.custid as CustomerID, c2.custName as ContactName
from customer1 as c1, customer2 as c2
where c1.custid = c2.custid"
);
I am looking for something very generic, where I can send in a string value of the stored proc that I want to execute. If this is not possible, is there any documentation on why it cannot be done this way? I need to prove why we cannot pass a string value of the name of the procedure to execute in Linq To Sql
DataContext.ExecuteCommand is not quite what you are looking for, as it only returns an int value. What you want instead is DataContext.ExecuteQuery, which is capable of executing a stored procedure and returning a dataset.
I would create a partial class for your DBML in which to store this function.
public List<T> GetDataNoParams(string procname)
{
var query = this.ExecuteQuery<T>("Exec " + procname);
return query.ToList();
}
public List<T> GetDataParams(string procname, Object[] parameters)
{
var query = this.ExecuteQuery<T>("Exec " + procname, parameters);
return query.ToList();
}
To call a stored procedure you would do:
GetDataNoParams("myprocedurename");
or
GetDataParams("myotherprocedure {0}, {1}, {2}", DateTime.Now, "sometextValue", 12345);
or
GetDataParams("myotherprocedure var1={0}, var2={1}, var3={2}", DateTime.Now, "sometextValue", 12345);
If you want to call procedures with no return value that is easy enough too, as I'm sure you can see, by creating a new method that doesn't store/return anything.
The inspiration came from http://msdn.microsoft.com/en-us/library/bb361109(v=vs.90).aspx.
The simplest answer to your question is that you can grab the Connection property of your MyDataContext and create and execute your own SqlCommands just like you would in straight up ADO.Net. I'm not sure if that will serve your purposes, especially if you want to retrieve entities from your LINQ to SQL model.
If you want to return entities from the model, then have a look at the DataContext.ExecuteCommand method.
When we drop a Table or StoredProcedure in our .dbml file it creates its class which communicates with the data layer and our business logic.
In Linq to SQL we have to have the StoredProcedures or Tables present in the .dbml file otherwise there is no way to call a generic method in Linq to SQL for calling a stored procedure by passing its name to a method.
But in ADO.Net we can do it (like you know)
SqlCommand cmd = new SqlCommand("myStoredProc", conn);