Trickly null validation for SQL Query - c#

"AND TT.[_TYPE] = CASE WHEN " + String.IsNullOrEmpty(lstTypeSearch.SelectedItem) ?
DBNull.Value} +
" IS NULL THEN TT.[_TYPE] ELSE " + lstTypeSearch.SelectedItem + " END ";
Above is a pseudo aspect of my query.
I need to do a validation of listbox item for null and then set as DBNull.Value, which is to be passed into CASE WHEN within SQL Query.
Any better way to achieve this? I am getting tons of String to Bool, Null to String conversion errors...
Further, is there anyway to pass DBNull.Value as a Parameter across data access layer?
EDIT: original query is in a Static class.
public static readonly string SqlGetItemsBy_Number_Capacity_Type =
"SELECT TT.[_NUMBER], " +
"TT.[CAPACITY], " +
"TT.[_TYPE], " +
"TS.[SESSIONE] " +
"FROM [ITEMS] AS TT, //some code
"WHERE //some code
"AND TT.[_TYPE] = CASE WHEN #Type IS NULL THEN TT.[_TYPE] ELSE #Type END";
If I run the same query in SQL Server, it works fine.
SELECT //some code
AND [_TYPE] = CASE WHEN NULL IS NULL THEN [_TYPE] ELSE #TYPE END
Finally: I decided to go with SQL Append and following to validate/set parameter.
string paramAppend;
var bld = lstTypeSearch.SelectedItem;
if (bld != null)
{
paramAppend = "AND TT.[_TYPE] = " + lstTypeSearch.SelectedItem.ToString();
}
else
paramAppend = "";

It looks like lstTypeSearch here represents a column name (hence parameterization: not an option), so the first thing I'd say is: make sure you white-list that. Rather than trying to do everything in one go, separate the two cases:
if(string.IsNullOrEmpty(lstTypeSearch.SelectedItem))
{
// nothing to check?
}
else
{
CheckValidColumn(lstTypeSearch.SelectedItem); // throws if white-list fails
sql.Append(" AND TT.[_TYPE] = [") // should probably add table alias
.Append(lstTypeSearch.SelectedItem)
.Append("]");
}
If I have misunderstood, and this isn't a column, then just parameterize:
if(string.IsNullOrEmpty(lstTypeSearch.SelectedItem))
{
// no restriction?
}
else
{
sql.Append(" AND TT.[_TYPE] = #type");
cmd.Parameters.AddWithValue("type", lstTypeSearch.SelectedItem);
}
// ...
cmd.CommandText = sql.ToString();

First, you should work with parameters, unless want to be exposed to potential sql injection threats.
Second, if you are building the sql dynamically in the code, a better approach would be to add the sql condition only if lstTypeSearch.SelectedItem is not an empty string or null. something like that:
sSql = "your sql query";
if(!String.IsNullOrEmpty(lstTypeSearch.SelectedItem)) {
sSql += "TT.[_TYPE] = '" + lstTypeSearch.SelectedItem + "'";
}
sSql += ";"
btw, what if the SelectedItem has a string that only contains white spaces? consider replacing the String.IsNullOrEmpty to String.IsNullOrWhiteSpace.

With the edit, you should be able to replace the last line with:
AND (#Type IS NULL OR TT.[_TYPE] = #Type)
far clearer, although frankly it doesn't make for great query cache plan usage or optimization; it would still, IMO, be better to just compose the correct SQL.
As for passing in the value:
object value = string.IsNullOrEmpty(lstTypeSearch.SelectedItem)
? (object)DBNull.Value : (object)lstTypeSearch.SelectedItem;
// ...
cmd.Parameters.AddWithValue("Type", value);

If null then feed that into the case when within SQL, so it will return all the data.
I'm interpreting this to mean that if there's no value specified then return all the rows, otherwise filter by that value.
I'm assuming lstTypeSearch.SelectedItem is your value and it's a string. If not, cast it or dig out the string value.
command.CommandText =
"SELECT * FROM TT WHERE #SelectedItem IS NULL OR TT.[_TYPE] = #SelectedItem";
var selectedItem = (string) lstTypeSearch.SelectedItem;
command.Parameters.AddWithValue("#SelectedItem",
String.IsNullOrEmpty(selectedItem) ? (object) DBNull.Value : selectedItem);
using (var dataReader = command.ExecuteReader())
{
...
}

Related

Using C# SqlCommand to Query SQL Table

I have a class SqlGetGroupCutRates which has a
public bool UpdateDefaultTarget(string param1)
method. In this method, I am using the SqlClient.SqlCommand class.
public bool UpdateDefaultTarget(string g1)
{
string myworkCenterCode = Form1.globalWorkCenter;
try
{
string connString = #"Data Source =" + server + "; Initial Catalog =" + redGreenDB + ";Integrated Security=SSPI";
// WHERE CUT_RATE_GROUP = #CUT_RATE_GROUP... WHAT IS #CUT_RATE_GROUP????
string myCommandString = "SELECT TOP 1 Default_Cut_Rate_Per_Hour FROM " + groupCutRateTable + " WHERE " +
"Cut_Rate_Group= #Cut_Rate_Group ORDER BY Record_Date DESC";
// SQL connection using the connString taking us to the redGreenDB
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
// Get the specific default cut rate from groupCutRateTable ("dbo.ABR_Tape_Cvt_Group_Cut_Rates") where Cut_Rate_Group (column)
using (SqlCommand cmd = new SqlCommand(myCommandString, conn))
{
cmd.Parameters.AddWithValue("Cut_Rate_Group", g1);
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
defaultFoundInQuery = true;
while (reader.Read())
{
int ordTarget = reader.GetOrdinal("Default_Cut_Rate_Per_Hour");
// Handle potential null database values:
if (!reader.IsDBNull(ordTarget))
{
int ord1 = reader.GetOrdinal("Default_Cut_Rate_Per_Hour");
default_Cut_Rate_Per_Hour = reader.GetDouble(ord1);
}
else
{
default_Cut_Rate_Per_Hour = 0;
}
}
}
// else if no data rows found.
else
{
default_Cut_Rate_Per_Hour = 0;
}
conn.Close();
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Problem getting Group Cut Rates from database: \n" + ex);
}
return defaultFoundInQuery;
}
In my myCommandString variable, you will notice it is set to the value
string myCommandString = "SELECT TOP 1 Default_Cut_Rate_Per_Hour FROM " + groupCutRateTable + " WHERE " +
"Cut_Rate_Group= #Cut_Rate_Group ORDER BY Record_Date DESC";
I am querying the top 1/first Default_Cut_Rate_Per_Hour column value from the groupCutRateTable WHERE the Cut_Rate_Group column value is #Cut_Rate_Group.
My question is... in this query... what is meant by the #Cut_Rate_Group part of the query? Is this just going to return the first Cut_Rate_Group column value? Essentially making this a "static" query that will always return the same value? Or is the syntax of #Cut_Rate_Group seen as dynamic? Meaning #Cut_Rate_Group is assigned a value somewhere in my code?
Apologies, I am very new to this. If you have any docs I could read further into this, I would also appreciate that so I can better understand any answer I may get. Thanks for taking the time to read this.
I am expecting that this syntax would make #Cut_Rate_Group dynamic in the sense that it is almost viewed as a variable that is assigned a value.
The statement
cmd.Parameters.AddWithValue("Cut_Rate_Group", g1)
creates the parameter #Cut_Rate_Group that is referred to in the select statement. Since its value comes from the parameter g1, it will be "dynamic" in that whatever value is passed in g1 will become the value of the parameter #Cut_Rate_Group used in the select statement.
The statement above could have been written
cmd.Parameters.AddWithValue("#Cut_Rate_Group", g1)
If you call UpdateDefaultTarget with the same value of g1, and no records have been deleted from the table, it will return the same record if no new records have a record date less than or equal to that original record.
However, not knowing what you are trying to accomplish, this may not be what you actual want to happen.
#Cut_Rate_Group is a sql paramter (and is dynamic like a variable). Parameterization of sql commands is to safe guard from sql injections.
It's value is added here
cmd.Parameters.AddWithValue("Cut_Rate_Group", g1)

Filter data from database by passing multiple nullable values

I have a table with n numbers of column, and i want to filter data by n numbers of nullable parameters,
instead of writing n times if else condition is there any way to resolve this problem either in c# (Linq,Entity framework) or in SQL with queries.
if any one have any solution please give the solution with an example.
Thanking you.
sure, you can have optional parameters in that sql.
The way you do this? You don't include the parameters in the sql, and then ONLY add the parameters as you need them! That way, you don't need all those extra conditions in the sql that is the condition, and then also the test for the #Param = null.
So, lets assume that I can search for City, or City + HotelName. And lets toss in a [x] Only search for Active Hotels. Or we search just for Hotelname. Or all 3 values!
As you WELL note, this becomes a harry ball of parameters that has to deal with only 3 choices (6 possible permutations). I can only imagine how bad this gets if you have 5 or 6 possible and optional value.
so, the simple solution? Well, we split the sql into a the base query, and then add the parameters on the fly. We STILL want (and get) strong type parameter checking, and thus get sql injection protection which of course is a important goal here.
We thus have this setup:
And the search then does this:
public void loadgrid()
{
string strSQL;
string strWhere;
strSQL = "select ID, FirstName, LastName, HotelName, City, Province from tblHotels";
strWhere = "";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, new SqlConnection(My.Settings.Test3)))
{
if (txtHotelName.Text != "")
{
// search for hotel name
strWhere = "(HotelName = #HotelName)";
cmdSQL.Parameters.Add("#HotelName", SqlDbType.NVarChar).Value = txtHotelName.Text;
}
if (txtCity.Text != "")
{
if (strWhere != "")
strWhere += " AND ";
strWhere += "(City = #City)";
cmdSQL.Parameters.Add("#City", SqlDbType.NVarChar).Value = txtCity.Text;
}
if (chkOnlyActive.Checked == true)
{
if (strWhere != "")
strWhere += " AND ";
strWhere += strWhere + "(HotelActive = #Active)";
cmdSQL.Parameters.Add("#Active", SqlDbType.Bit).Value = 1;
}
if (strWhere != "")
cmdSQL.CommandText = strSQL + " WHERE " + strWhere;
cmdSQL.Connection.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader);
ListView1.DataSource = rstData;
ListView1.DataBind();
}
}
So note how we simply build up the where clause. And you note that there is NOTHING that prevents us from changing the sql command text - and we are also 100% able to add parameters on the fly (adding them does not force a check against the sql - only at execute time.
As a result? We can add 5 more criteria. They are optional, they don't require us to make a huge long sql query with a gazillion parameters that we may will not want to use or even need.
And as above shows, we there are NEVER sting concatenation of the user inputs - they ALWAYS are used ONLY with parameter values.
So, for any text box, check box, combo box or whatever? We simply ignore them when they are not filled out. They are thus all optional, and quite much ignored in our code. The above setup thus would allow us with easy to add 2 or 5 more optional parameters.
Note in above, we always "check" if the where clause already has some value - and if so, then we add the " AND " clause in front. We could I suppose use " OR " here, but it depends on the kind of search you want.
I nice 'trick' that can be used in both SQL statements and LINQ queries is to allow nulls on your query params and then check for a matching value or null on each parameter.
We make our params nullable and check each against their respective field/property or for null.
Basically, we tell the query to give us all records where the input parameter matches the property value OR if the input parameter is null we short circuit that param essentially causing our query to ignore that param. This effectively gives a parameter that is treated as optional when it's null and not optional otherwise.
Using this method you can easily add more optional parameters.
IList<ThingToQuery> things = new List<ThingToQuery>();
things.Add(new ThingToQuery(){ Property1 = "Thing1", Property2 = 100, Property3 = new DateTime(2001,1,1)});
things.Add(new ThingToQuery() { Property1 = "Thing2", Property2 = 100, Property3 = new DateTime(2002, 2, 2) });
things.Add(new ThingToQuery() { Property1 = "Thing3", Property2 = 300, Property3 = new DateTime(2003, 3, 3) });
// query sample #1 - prepare params
string queryParam1 = "Thing1";
int? queryParam2 = 100;
DateTime? queryParam3 = null;
// in our query we check for a matching value or if the param is null
List<ThingToQuery> results = things.Where(t => (t.Property1 == queryParam1 || queryParam1 == null)
&& (t.Property2 == queryParam2 || queryParam2 == null)
&& (t.Property3 == queryParam3 || queryParam3 == null)
).ToList();
// query sample #1 results
// Thing1, 100, 1/1/2001 12:00:00 AM
// query sample #2 - prepare params
string queryParam1 = null;
int? queryParam2 = 100;
DateTime? queryParam3 = null;
// query sample #2 results
// Thing1, 100, 1/1/2001 12:00:00 AM
// Thing2, 100, 2/2/2002 12:00:00 AM
A simple SQL example...
SELECT * FROM Users u
WHERE (u.UserName = #UserName OR #UserName IS NULL)
OR (u.FavoriteColor = #FavColor OR #FavColor IS NULL)

Parameterized query not working in C#

I am currently in a corner and have no idea why the following code will not execute properly and update the database (Access).
newUser = All of the new user's data including their ID
list = Contains a list of GermanResources (class) entries that correspond to the pages checkboxes. Class includes .Name (text value of checkbox) and .Value (checked? 1 or 0)
I want to update the database with the checkbox value of each GermanResource.
IF i replace #acc_Value with the value 1 this code works. It seems to not work with the first parameter in place. Debugging this showed me that everything had the proper values at the proper times and since "1" worked I know the data types are not mismatched.
Note: There were no errors with or without the parameter in place.
I would appreciate any input about this.
This is one of the CommandTexts that are generated:
UPDATE VMS_GRM_GermanResource_Access SET VTOFZN = #acc_Value WHERE UserId = #userId
private bool NewUser_Insert_GermanResourceAccess(OleDbConnection connection, User newUser, List<GermanResource> list)
{
bool result = false;
try
{
foreach (var item in list)
{
string column = item.Name.Replace(" ", "");
string query = #"UPDATE VMS_GRM_GermanResource_Access SET " + column + " = #acc_Value WHERE UserId = #userId";
OleDbCommand command = new OleDbCommand(query, connection);
command.Parameters.AddWithValue("#userId", newUser.Id);
command.Parameters.Add(new OleDbParameter("#acc_Value", OleDbType.Integer, 1));
command.Parameters["#acc_Value"].Value = item.Access;
command.ExecuteNonQuery();
}
result = true;
}
catch (OleDbException ex)
{
UADConnection.Close();
MessageBox.Show(ex.ErrorCode.ToString() + ": " + ex.Message);
return result;
}
return result;
}
Use this to prepare sql statement :-
string query = #"UPDATE VMS_GRM_GermanResource_Access SET column_name=" +
#acc_Value + " WHERE UserId = " +#userId+";
#Tetsuya Yamamoto:
OLEDB parameters were not in order according to the query. Swapping them around to match the order in the query set things straight. All good again and thanks for everyone's inputs.

LINQ to SQL custom query builder

It was easy to build a custom query like this with ADO.NET:
SqlCommand.CommandText = "SELECT Column" + variable1 + ", Column" + Variable2 + " FROM TABLE";
Is that able to do so in LINQ to SQL?
Thanks
No there is no common way to build a dynamic query.
A method has to have a known, specific return type. That type can be
System.Object but then you have to use a lot of ugly reflection code
to actually get the members. And in this case you'd also have to use a
lot of ugly reflection expression tree code to generate the return
value.
If you're trying to dynamically generate the columns on the UI side -
stop doing that. Define the columns at design time, then simply
show/hide the columns you actually need/want the user to see. Have
your query return all of the columns that might be visible.
Unless you're noticing a serious performance problem selecting all of
the data columns (in which case, you probably have non-covering index
issues at the database level) then you will be far better off with
this approach. It's perfectly fine to generate predicates and sort
orders dynamically but you really don't want to do this with the
output list.
More about this
Yes You can do something like that with Dynamic query with Linq.
This is an example that you can build a custom query with Dynamic query with Linq:
string strWhere = string.Empty;
string strOrderBy = string.Empty;
if (!string.IsNullOrEmpty(txtAddress.Text))
strWhere = "Address.StartsWith(\"" + txtAddress.Text + "\")";
if (!string.IsNullOrEmpty(txtEmpId.Text))
{
if(!string.IsNullOrEmpty(strWhere ))
strWhere = " And ";
strWhere = "Id = " + txtEmpId.Text;
}
if (!string.IsNullOrEmpty(txtDesc.Text))
{
if (!string.IsNullOrEmpty(strWhere))
strWhere = " And ";
strWhere = "Desc.StartsWith(\"" + txtDesc.Text + "\")";
}
if (!string.IsNullOrEmpty(txtName.Text))
{
if (!string.IsNullOrEmpty(strWhere))
strWhere = " And ";
strWhere = "Name.StartsWith(\"" + txtName.Text + "\")";
}
EmployeeDataContext edb = new EmployeeDataContext();
var emp = edb.Employees.Where(strWhere);
grdEmployee.DataSource = emp.ToList();
grdEmployee.DataBind();
For more information you can check this page.

Best way to build a query with condition on code-behind?

How to make this code properly? I am not satisfied with this code, I'm lost.
I give you a simple example but the query is more complexe.
Thanks in advance.
string aValue;
string queryA;
string queryB;
string finalQuery;
string queryA = #"SELECT column1 FROM table1 WHERE column1=";
queryA += aValue;
string queryB = #"SELECT column1, column2,"
if (aValue == "all"){
queryB += #"column3";
}
queryB += #"FROM table1 WHERE column1=";
queryB += #"'" +aValue+ "'";
private void exportExcel(){
// change the value with a dropdownlist
if (ddlType.selectedIndex(1))
aValue = "typeA";
else if(ddlType.selectedIndex(2))
aValue = "typeB";
else
aValue = "all";
// select the query
if (aValue == "typeA")
finalQuery = queryA;
else if (aValue == "typeB")
finalQuery = queryB;
ExecQUery(finalQuery);
}
In both Java and C# (and pretty much any other platform) you should definitely not include the values directly in the SQL. That's opening up the way to SQL injection attacks, and also makes dealing with formatting for dates, times and numbers tricky.
Instead, you should use parameterized SQL, specifying the values in the parameters. How you do that varies between Java and C#, but the principle is the same.
Another approach on both platforms is to use an ORM of some description rather than building queries by hand. For example, in .NET you might want to use a LINQ provider of some description, and in Java you might want to use something like Hibernate. Either way you get to express your queries at a higher level of abstraction than just the raw SQL.
It's hard to give much more concrete advice without knowing what platform you're really using (or database) and without a real query to look at.
One small change you can do is set value attribute of dropdownlist to typeA,TypeB, etc.. and get rid of the initial if conditions and variables.
eg:
if(ddlType.selectedValue.toString()=="typeA")
finalQuery = queryA;
if(ddlType.selectedValue.toString()=="typeB")
finalQuery = queryB;
I usually load it from a resource file. This gives you some freedom to change the queries (this in case you don't need to generate it dynamically with if blocks). In source code I use formatting ending my line with a comment line in order to avoid my IDE to concatenate or put it all in one like like:
String sql = "select " + //
" * " + //
"from "+ //
" employee " + //
"where " + //
" salary > :minSal " + //
" and startDate > :minStartDate";
And in case of conditional part I just add it with a if block. But for where statements I just add one default "1=1" in order to proceed with additional limitations, so if there is no additional limitations the query will still be valid. Suppose both where statements in the SQL bellow were added conditionally:
String sql = "select " + //
" * " + //
"from "+ //
" employee " + //
"where 1 = 1 ";
Until here you have your base SQL, valid, that means if not condition is added it will still be valid.
Suppose you will add salary limitation just in case if it is informed:
if (salary != null) {
sql += "and salary > :minSalary";
parameters.put("minSalary", salary);
}
As you can see in the same condition I add a new expression to my SQL and a parameter to a map that will be used later in execute to set the parameters to the query, that avoids you to create a second if statement just to set this parameter.
Another approach that you could take is build the entire SQL and before the execution ask for the prepared statement which parameters it needs as input and provide them. In java you can do it with:
http://download.oracle.com/javase/1.4.2/docs/api/java/sql/PreparedStatement.html#getParameterMetaData%28%29
I know that is not the case but if ORM is used, is common to have Builders for queries and this turns this task much easier, for example in Hibernate you could have something like:
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();
As it is documented at:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querycriteria.html
That means you could do this:
Criteria c = sess.createCriteria(Cat.class)
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50);
if (name != null) {
c.add( Restrictions.like("name", name);
}
List cats = c.list();

Categories