C# sql query stop at cmd.execute - c#
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.
Related
How to get a key from a specific database row when I double click an item on a list?
Basically I have this list of students (alunos) and I want to double click in one of the students and I want it to show a MessageBox containing: Student No.: {student no. from the selected student here} Name: {student name from the selected student here} etc... Here's the code I have: void lstAlunos_MouseDoubleClick(object sender, MouseEventArgs e) { string query = "SELECT * FROM (" + "SELECT" + "ROW_NUMBER() AS rownumber," + "columns" + "FROM Alunos" + ") AS foo" + "WHERE rownumber = " + lstAlunos.SelectedIndex; using (connection = new SqlConnection(connectionString)) using (SqlCommand command = new SqlCommand(query, connection)) using (var reader = command.ExecuteReader()) { connection.Open(); reader.Read(); string numAluno = reader.GetString(0); string nomeAluno = reader.GetString(1); string apelidoAluno = reader.GetString(2); string contactoAluno = reader.GetString(3); string emailAluno = reader.GetString(4); } int index = this.lstAlunos.IndexFromPoint(e.Location); if (index != System.Windows.Forms.ListBox.NoMatches) { MessageBox.Show("Nome: " + nomeAluno); } }
Add spaces to the ends of the strings you're concatenating. Right now this segment: "columns" + "FROM" + will give you "columnsFROM" which is obviously bad SQL. Some other suggestions: Add error handling. The exception you got from this should give you a clue as to where the problem is. Use parameters instead of concatenating string values. It will protect you from SQL Injection attacks and take care of formatting for you. Don't use ROW_NUMBER() without some sore of explicit order. THere's no guarantee that you'll get the rows in the same order that they were saved in previously. Use an ID column instead.
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);
How to pass a C# variable with apostrophe through MySql
I'm having a problem inputting variables into my database. I've seen other posts on how to pass a variable through by just escaping it, but those solutions do not apply because I am getting my variable's through an API. I'm cycling though data with a foreach loop by the way. level = "" + x.Account_Level + ""; name = "" + x.name + ""; command.CommandText = "INSERT INTO `data` (`level`, `name`) VALUES(" + level + ", " + name + ")"; command.ExecuteNonQuery(); Sometimes, a variable will come back with an apostrophe and will screw up the code. Is it possible to insert a slash before every apostrophe or is there a way like in PHP to just push the whole variable through with single quotes? Thanks! Edit: Would this work? I think I need to add the i to change the name of the parameter each loop, due to it claiming the parameter as already declared. using (var web = new WebClient()) { web.Encoding = System.Text.Encoding.UTF8; var jsonString = responseFromServer; var jss = new JavaScriptSerializer(); var MatchesList = jss.Deserialize<List<Matches>>(jsonString); string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;"; MySqlConnection connect = new MySqlConnection(connectString); MySqlCommand command = connect.CreateCommand(); int i = 1; connect.Open(); foreach (Matches x in MatchesList) { command.CommandText = "INSERT INTO `data` (`level`, `name`) VALUES(?level" + i + ", ?name" + i + ")"; command.Parameters.AddWithValue("level" + i, x.Account_Level); command.Parameters.AddWithValue("mode" + i, x.name); command.ExecuteNonQuery(); i++; } connect.Close(); }
The quick and dirty fix is to use something like: level = level.Replace("'","whatever"); but there are still problems with that. It won't catch other bad characters and it probably won't even work for edge cases on the apostrophe. The best solution is to not construct queries that way. Instead, learn how to use parameterised queries so that SQL injection attacks are impossible, and the parameters work no matter what you put in them (within reason, of course). For example (off the top of my head so may need some debugging): MySqlCommand cmd = new MySqlCommand( "insert into data (level, name) values (?lvl, ?nm)", con); cmd.Parameters.Add(new MySqlParameter("lvl", level)); cmd.Parameters.Add(new MySqlParameter("nm", name)); cmd.ExecuteNonQuery();
C# SQL Multiple IN Selectors
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.