C# SQL Multiple IN Selectors - c#

My output query looks like:
select * from Calls where CallerID = "User.Name" and Endpoint in ("C,D,V") order by JoinTime desc;
My C# query string with parameters looks like this:
string SQLQuery = "select * from Calls where CallerID = #UserName and EndpointType in ({0}) order by JoinTime desc";
The code to add the parameters looks like this:
...
string[] Endpoints = new string[] {"C","D","V"}; //EXAMPLE string array
string UserNameStr = "User.Name"; //EXAMPLE string UserNameStr value
...
string[] paramNames = Endpoints.Select((s, i) => "#endpoint" + i.ToString()).ToArray();
string inClause = string.Join(",", paramNames);
using (cmd = new MySqlCommand((string.Format(SQL, inClause)),connection))
{
for (int i = 0; i < paramNames.Length; i++)
{
cmd.Parameters.AddWithValue(paramNames[i], Endpoints[i]);
}
}
cmd.Parameters.Add("#UserName", MySqlDbType.String).Value = UserNameStr;
MySqlDataReader dataReader = cmd.ExecuteReader();
...
But what if I wanted to add another IN operator so the output query would look like this ;
select * from Calls where CallerID = "User.Name" and Endpoint in ("C","D","V") and ConferenceCall in ("D","C") order by JoinTime desc;
How do I do that? Is there some features in Linq that could be used?

Same way you do it the first time:
// endpoints = comma-delimited list of endpoints
// conferenceCalls = comma-delimited list of conference calls
string SQLQuery = "select * from Calls " +
"where CallerID = #UserName and " +
"EndpointType in ({0}) and " +
"ConferenceCall in ({1}) " +
"order by JoinTime desc";
cmd = new MySqlCommand((string.Format(SQL, endpoints, conferenceCalls)),connection);
As noted in comments, be VERY careful that you validate the strings to avoid SQL injection attacks. There's not a direct way to pass a collection as a SQL parameter. There are ways to do it (passing a delimited string and parsing it is the most common I've seen), but if you are in control of setting the value and validate the data then you should be fine.

Related

C# sql query stop at cmd.execute

Hello I've made a method that takes search parameters and then returns a query. This was done using ADO.net now what I have noticed is that my method abruptly ends on the cmd.execute. Here is my method.
public class SQLStringSearchService
{
string connectionString = string
public async Task<List<TransactionJournal>> SearchArchivedTransactionJournal(DateTime transactionDate, string Region, string MCC, string MerchantID, string TxnCurrency, string TerminalID, decimal TxnAmount, string BIN, string MsgType, string MaskedPan, string ProcessingCode, string ClearPan, string ResponseCode, string AuthorizationCode, string EntryMode, [FromQuery] PaginationDTO pagination)
{
var monthString = string.Empty;
if (transactionDate.Month < 10)
{
monthString = transactionDate.Month.ToString().Insert(0,"0");
}
else
{
monthString = transactionDate.Month.ToString();
}
var DbTable = "TRANSACTIONJOURNAL_" + transactionDate.Year.ToString() + monthString;
var queryable = new List<TransactionJournal>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand())
{
connection.Open();
cmd.Connection = connection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Select * From Table = #Table Where TransactionDateTime <= #DateTime and AcquirerID = #Region and MerchantCategoryCode = #MCC and MerchantID = #MerchantId and Currency = #TxnCurrency and TerminalID = #TerminalId and " +
"TransactionAmount = #TxnAmount and Bin = #Bin and MessageType = #MsgType and ProcessingCode = #ProcessingCode and PANM = #Panm and PAN = #Pan and ResponseCode = #ResponseCode and AuthorizationCode = #AuthCode and ResponseCode = #ResponseCode";
cmd.Parameters.AddWithValue("#Table", DbTable);
cmd.Parameters.AddWithValue("#DateTime", transactionDate);
cmd.Parameters.AddWithValue("#Region", Region);
cmd.Parameters.AddWithValue("#MCC", MCC);
cmd.Parameters.AddWithValue("#MerchantId", MerchantID);
cmd.Parameters.AddWithValue("#TxnCurrency", TxnCurrency);
cmd.Parameters.AddWithValue("#TerminalId", TerminalID);
cmd.Parameters.AddWithValue("#TxnAmount", TxnAmount);
cmd.Parameters.AddWithValue("#Bin", BIN);
cmd.Parameters.AddWithValue("#MsgType", MsgType);
cmd.Parameters.AddWithValue("#ProcessingCode", ProcessingCode);
cmd.Parameters.AddWithValue("#Panm", MaskedPan);
cmd.Parameters.AddWithValue("#Pan", ClearPan);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
cmd.Parameters.AddWithValue("#AuthCode", AuthorizationCode);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
queryable = (List<TransactionJournal>)cmd.ExecuteScalar();
cmd.Dispose();
}
}
var orderedQuery = queryable.OrderByDescending(x=> x.TransactionDateTime).AsQueryable();
await HttpContextExtensions.GetPage<TransactionJournal>(orderedQuery, pagination.Page, pagination.QuantityPerPage);
return await orderedQuery.Paginate(pagination).ToListAsync();
}
}
I have tried debugging and the method just ends, I can't seem to figure out what the problem could be. If someone could show me an example of maybe the correct way to so this I would greatly appreciate it. Thanks!
Code doesn't just stop but any time it seems like it does, it's probably encountering an exception that isn't handled properly, inside of a coding construct that cannot tolerate it - I most often see this if there is some background threading involved, the thread hits an exception, it's stopped and the exception is transferred to whatever was managing the thread (which then does nothing with it). The thread just goes away and VS stops tracking it. You might find it switches to another thread but it does look like the code "just stopped"- probably we'd find that this method was called without await or its in a backgrounder worker that doesn't retrieve the exception
All in, this code has some flaws and cannot work. As per the comments the sql is invalid. You also wouldn't/couldn't executeScalar and hope to get a list out of it. By definition scalar is a single value, the top left cell of any result set- there's no scope for it to be a list of values. If you want a list you have to executeReader and fill the list ...
If you enable break when thrown (go in the debug menu , look for exceptions window, put a tick next to Common Language Runtime - now as soon as any exception is raised, handled or not, the code will stop. You'll probably find now that your code is stopping on a sql exception, but at least you can fix it and move on. It's recommend you at least wrap your executeXxx in a try catch...
You seem to have an invalid query. You can't pass table as a variable the way you're trying to. To achieve that type of functionality you have to use string.Fromat.
Try something like:
using (var cmd = new SqlCommand())
{
connection.Open();
cmd.Connection = connection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = string.Join(
string.Fromat("SELECT * FROM {0} ",DbTable),
"WHERE TransactionDateTime <= #DateTime ",
"AND AcquirerID = #Region ",
"AND MerchantCategoryCode = #MCC",
"AND MerchantID = #MerchantId ",
"AND Currency = #TxnCurrency ",
"AND TerminalID = #TerminalId ",
"AND TransactionAmount = #TxnAmount ",
"AND Bin = #Bin ",
"AND MessageType = #MsgType ",
"AND ProcessingCode = #ProcessingCode ",
"AND PANM = #Panm ",
"AND PAN = #Pan ",
"AND ResponseCode = #ResponseCode ",
"AND AuthorizationCode = #AuthCode ",
"AND ResponseCode = #ResponseCode");
cmd.Parameters.AddWithValue("#DateTime", transactionDate);
cmd.Parameters.AddWithValue("#Region", Region);
cmd.Parameters.AddWithValue("#MCC", MCC);
cmd.Parameters.AddWithValue("#MerchantId", MerchantID);
cmd.Parameters.AddWithValue("#TxnCurrency", TxnCurrency);
cmd.Parameters.AddWithValue("#TerminalId", TerminalID);
cmd.Parameters.AddWithValue("#TxnAmount", TxnAmount);
cmd.Parameters.AddWithValue("#Bin", BIN);
cmd.Parameters.AddWithValue("#MsgType", MsgType);
cmd.Parameters.AddWithValue("#ProcessingCode", ProcessingCode);
cmd.Parameters.AddWithValue("#Panm", MaskedPan);
cmd.Parameters.AddWithValue("#Pan", ClearPan);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
cmd.Parameters.AddWithValue("#AuthCode", AuthorizationCode);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
queryable = (List<TransactionJournal>)cmd.ExecuteScalar();
}
You don't have to use the string.Join the way I did. I just wanted to split the query in parts to make it a bit more readable for me. You can format that all sorts of ways.
When possible, I would recommend that you write your queries and test them in SQL server or local instance. Then you can use it to create your command text string.
Something like this might have helped you spot the issue:
DECLARE #Table varchar(50) = 'TRANSACTIONJOURNAL_202003',
#DateTime datetime = GETDATE(),
-- ... And so on with the rest of the variables you need
-- you have lots of variables
-- you get the gist
SELECT * FROM Table = #Table
WHERE TransactionDateTime <= #DateTime
AND AcquirerID = #Region
AND MerchantCategoryCode = #MCC
AND MerchantID = #MerchantId
AND Currency = #TxnCurrency
AND TerminalID = #TerminalId
AND TransactionAmount = #TxnAmount
AND Bin = #Bin
AND MessageType = #MsgType
AND ProcessingCode = #ProcessingCode
AND PANM = #Panm
AND PAN = #Pan
AND ResponseCode = #ResponseCode
AND AuthorizationCode = #AuthCode
AND ResponseCode = #ResponseCode;
One more thing. I would recommend not using SELECT *. Instead I'd explicitly write out the column names. If they ever add another column to that table it could break things. Specially if they change the order of the columns.
Hope this helps.

Pass a list as a parameter to an oracle query

This code works for a query parameter that is a single value:
public void GetById(long id)
{
var sql = "SELECT * FROM table1 WHERE id = :id";
using(var cmd = new OracleCommand(sql, oracleConnection)) {
cmd.Parameters.Add(new OracleParameter("id", id));
...
}
}
What's the equivalent for an array or list of values?
public void GetByIds(long[] ids)
{
var sql = "SELECT * FROM table1 WHERE id IN (:ids)";
using(var cmd = new OracleCommand(sql, oracleConnection)) {
cmd.Parameters.Add(new OracleParameter("ids", /* ??? */));
...
}
}
In case it's not clear, I'm looking for something that will work like this:
sql = "SELECT * FROM table1 WHERE id IN (" + string.Join(',', ids) + ")";
but as a clean parameterized query.
You coud send the list of parameters as string and use a regular expresion to separate the id's. Example:
select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level) from dual
So
sql = "SELECT * FROM table1 WHERE id IN (
select regexp_substr(:ids,'[^,]+', 1, level) from dual
)";
How to split comma separated string and pass to IN clause of select statement

C# replace parameters in query with defined values [duplicate]

This question already has answers here:
parameterized queries vs. SQL injection
(4 answers)
Closed 6 years ago.
string[] theParms = ['parm1', 'parm2'];
string theQuery = "SELECT something, somethingAgain " +
"FROM aDBTable " +
"WHERE something = '{?}'" +
"AND something <> '{?}'";
I am needing to replace the {?}'s with the defined parms in theParms.
Is there some type of loop in C# that I can use in order to loop through the string and replace each found {?} with the corresponding parm value?
Something like this:
First loop:
SELECT something, somethingAgain
FROM aDBTable
WHERE something = 'parm1' AND something <> '{?}'
Second loop:
SELECT something, somethingAgain
FROM aDBTable
WHERE something = 'parm1' AND something <> 'parm2'
Is there some type of REGEX or common framework function that can do the above?
sql injection check
bool injectionCheckin = new injectionCheck().inCheck(theFinalQuery);
public class injectionCheck
{
public bool inCheck(string queryString)
{
var badWords = new[] {
"EXEC", "EXECUTE", ";", "-", "*", "--", "#",
"UNION", "DROP","DELETE", "UPDATE", "INSERT",
"MASTER", "TABLE", "XP_CMDSHELL", "CREATE",
"XP_FIXEDDRIVES", "SYSCOLUMNS", "SYSOBJECTS",
"BC_HOME_ADDRESS1", "BC_HOME_ADDRESS2", "BC_HOME_CITY", "BC_HOME_COUNTY", "BC_HOME_POSTAL", "BC_MAIL_ADDRESS1",
"BC_MAIL_ADDRESS2", "BC_MAIL_CITY", "BC_MAIL_COUNTY", "BC_MAIL_POSTAL", "BC_MAIL_STATE", "FLSA_STATUS", "GRADE",
"GRADE_ENTRY_DT", "HIGHEST_EDUC_LVL", "LAST_INCREASE_DT", "BC_SALP_DESCR", "BC_SALP_DESCRSHORT", "SAL_ADMIN_PLAN"
};
string pattern = "(?<!\\w)(" + Regex.Escape(badWords[0]);
foreach (var key in badWords.Skip(1))
{
pattern += "|" + Regex.Escape(key);
}
pattern += ")(?!\\w)";
dynamic _tmpCount = Regex.Matches(queryString, pattern, RegexOptions.IgnoreCase).Count;
if (_tmpCount >= 1)
return true;
else
return false;
}
}
Always create Sql-commands by parameterized queries:
using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
using (SqlCommand cmd = conn.CreateCommand())
{
var #params = new Dictionary<string, object>{
{ "something", myValue },
{ "somethingDifferent", anotherValue },
};
cmd.CommandText = "SELECT something, somethingAgain " +
"FROM aDBTable " +
"WHERE something = #something'" +
"AND something <> #somethingDifferent'";
foreach (KeyValuePair<string, object> item in values)
{
cmd.Parameters.AddWithValue("#" + item.Key, item.Value);
}
DataTable table = new DataTable();
using (var reader = cmd.ExecuteReader())
{
table.Load(reader);
return table;
}
}
}
This prevents all sort of SqlInjection and you won´t any weird checks as yours with the badlist which is quite messy and does not really prevent you, you can easily bypass the list with some escaping for instance. In particular: why do you want to write your own validation when there allready are ready-to-use methods that do exactly what you want?
Why not just use String.Format?
string[] theParms = new string[] { "parm1", "parm2" };
string theQuery = #"SELECT something, somethingAgain
FROM aDBTable
WHERE something = '{0}'
AND something <> '{1}'";
var res = string.Format(theQuery, theParms);
Result:
SELECT something, somethingAgain
FROM aDBTable
WHERE something = 'parm1'
AND something <> 'parm2'
If you want to do it in either case, you could do it without a loopy as follows.
string theQuery = String.Format( "SELECT something, somethingAgain " +
"FROM aDBTable " +
"WHERE something = '{0}'" +
"AND something <> '{1}'",
theParams[0], theParams[1] );
Okay, to avoid Injection and all that, why don't you do it like this:
string[] theParms = // initialization
string theQuery = // initialization
SqlCommand cmd = new SqlCommand(/* enter connection string */, theQuery)
for(int i = 0; i < theParams.Length; i++)
{
int index = cmd.Text.IndexOf("{?}");
if(index > -1)
{
string pName = string.Format("#p{0}", i);
cmd.Text = cmd.Text.Remove(index, 3).Insert(index, pName);
cmd.Parameters.Add(new SqlParameter() { Name = pName, Value = theParms[i] });
}
}
That should avoid any manual injection checks alltogether... at least if you can't get the query pre-compiled and have to load it at runtime. Otherwise just formulate the SqlCommand's text appropriately and you'll not need a loop or anything. Just a simple initialization:
SqlCommand cmd = new SqlCommand(/* enter connection string */, "SELECT something, somethingAgain FROM aDBTable WHERE something = #p0 AND something <> #p1");
cmd.Parameters.Add(new SqlParameter() { Name = "#p0", Value = theParms[0] });
cmd.Parameters.Add(new SqlParameter() { Name = "#p1", Value = theParms[1] });
You can use IndexOf and substrings to find each instance
for(int i = 0; i < theParms.GetLength(0); i++)
{
string[] tempStrings = new string[]{ theQuery.Substring(0,theQuery.IndexOf("{?}") - 1),
theQuery.Substring(theQuery.IndexOf("{?}"), 3),
theQuery.Substring(theQuery.IndexOf("{?}") + 4) }
tempStrings[1] = tempStrings[1].Replace("{?}", theParms[i]);
theQuery = String.Join("", tempStrings);
}
Though seeing as you check for injection afterwards it is definitely much better to use String.Format
You don't have to handle it by yourself. Instead, ADO.NET allows you define parameters and set their values. See the sample here .MSDN

Can a C# SQL Parameter Contain '='

So I am trying to create parameters to pass to my query and was wondering if I could pass a parameter that contains an '=' sign in it that replaces the traditional '=' sign in the SQL statement. It would be like the following:
string aoi = "=" + comboBox1.Text;
string comp = "=" + comboBox2.Text;
//Default values of their respective Combo Boxes.
if(aoi == "Area Of Interest")
aoi = "!= NULL";
if (comp == "Company")
comp = "!= NULL";
cmd.CommandText = "SELECT * FROM JobListInfo" +
"WHERE AreaOfInterest #AOI AND Company #Company";
cmd.Parameters.AddWithValue("#AOI", aoi);
cmd.Parameters.AddWithValue("#Company", comp);
The reason I am asking is because if the user doesn't change the default value, I want it to pull all the records (with respect to the rest of the SQL statement). I understand I could create an OR statement, but I have three other parameters I would like to pass as well and didn't want to create 15 OR cases.
EDIT: I found a solution to my problem. I changed the '=' to Like and changed the strings to '%' if they didn't select a value. This shouldn't cause any SQL injection issues, right?
Sample Code:
if(aoi == "Area of Interest")
aoi = "%"
cmd.CommandText = "SELECT * FROM JobListInfo " +
"WHERE AreaOfInterest LIKE #AOI";
cmd.Parameters.AddWithValue("#AOI", aoi);
Not via a parameter. If this were allowed, it would potentially introduce SQL injection vulnerabilities.
A potential solution would be to dynamically create the CommandText string by appending database column names and parameter placeholders to the query's WHERE clause.
WARNING: Do not append input values to the WHERE clause of your query string! This will leave you vulnerable to SQL injection. Instead, append parameter placeholders and then populate them using the cmd.Parameters.AddWithValue() method.
That being said, something like the code below might work. However, it would depend on you selecting a single default value for your combo-boxes. Consequently, you would need to use UI labels instead of default values to describe the combo-boxes in your app.
string MY_DEFAULT_VALUE = 'Pick One:';
string queryString = "SELECT * FROM my_table";
//Populate Dictionary:
Dictionary<string,ComboBox > columnDictionary= new Dictionary<string, ComboBox>();
columnDictionary.Add("COL_A", comboBox1);
columnDictionary.Add("COL_B", comboBox2);
columnDictionary.Add("COL_C", comboBox3);
//etc...
List<KeyValuePair<string, ComboBox>> appendedColumns = new List<KeyValuePair<string, ComboBox>>();
foreach (KeyValuePair<string, ComboBox> entry in columnDictionary)
{
if (!String.Equals(entry.Value.Text, MY_DEFAULT_VALUE, StringComparison.OrdinalIgnoreCase))
{
string currentColumnName = entry.Key;
string currentColumnParameter = "#" + entry.Key;
if (appendedColumns.Count>1)
{
queryString += " AND ";
}
else
{
queryString += " WHERE ";
}
queryString += currentColumnName + " = " + currentColumnParameter;
appendedColumns.Add(entry);
}
}
cmd.CommandText = queryString;
if (appendedColumns.Count > 0)
{
foreach (KeyValuePair<string, ComboBox> entry in appendedColumns)
{
string currentColumnParameter = "#" + entry.Key;
string currentParameterValue = entry.Value.Text;
cmd.Parameters.AddWithValue(currentColumnParameter, currentParameterValue);
}
}
//Continue on your way...
Short answer to your question is, you cannot use '=' as you have shown. So change your code like this;
string aoi = comboBox1.Text;
string comp = comboBox2.Text;
string sql;
if (aoi == "Area Of Interest" && comp == "Company")
{
sql = #"SELECT * FROM JobListInfo WHERE AreaOfInterest Is Not NULL AND Company Is Not NULL";
}
else if(....)
{
sql =............................
}
else
{
sql = #"SELECT * FROM JobListIn WHERE AreaOfInterest = #AOI AND Company = #Company";
}
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("#AOI", aoi);
cmd.Parameters.AddWithValue("#Company", comp);

using array in SQLReader

in the following code , Im trying to assign reader(r33) into array so i can use the values of this array in the next sql command. can you help me doing this ?
//connection
SqlConnection c = new SqlConnection("Data Source=localhost\\sqlexpress;Initial Catalog=BookStoreDataBase1;Integrated Security=True;Pooling=False;");
c.Open();
//first statment
string raf = string.Format("Select Id from [Order] WHERE customerID={0}", k);
SqlCommand comm1 = new SqlCommand(raf, c);
SqlDataReader r33 = comm1.ExecuteReader();
r33.Read();
int [] k2 = r3.GetInt32(0);
r33.Close();
//second statment
string raf4 = string.Format("Select * from orderDetails WHERE orderId={0}", k2);
SqlCommand comm14 = new SqlCommand(raf4, c);
SqlDataReader r333 = comm14.ExecuteReader();
not very sure what you asked, but as far as I understood, you first query will return multiple rows containing different IDs and you want to use them in second command. Basic way of doing it would be :
string IDs = "(";
while (r33.read())
{
IDs += r33.GetString(0) + ",";
}
IDs += ")"
IDs = IDs.Replace(",)",")");
and then use IN keyword for multiple IDs you got
string raf4 = string.Format("Select * from orderDetails WHERE orderId IN {0}", IDs);

Categories

Resources