Constructing a good search query using system.data.oracleclient - asp.net

I am constructing a search function in a class to be used by several of our asp pages. The idea is simple, take a search term from the user and query the database for the item. Currently I am doing this the wrong way, which is vulnerable to SQL injection attacks (and ELMAH is in there to save the day if something goes wrong):
Public Shared Function SearchByName(ByVal searchterm As String) As DataTable
SearchByName = New DataTable
Dim con As New OracleConnection(System.Configuration.ConfigurationManager.ConnectionStrings("OracleDB").ConnectionString)
Try
con.Open()
Dim SqlStr As String = "select ID_ELEMENT, ELEMENT_NAME from table_of_elements where upper(ELEMENT_NAME) like upper('%" & searchterm & "%')"
Dim cmd As New OracleCommand(SqlStr, con)
SearchByName.Load(cmd.ExecuteReader)
Catch ex As Exception
Elmah.ErrorSignal.FromCurrentContext().Raise(ex)
End Try
con.Close()
con.Dispose()
Return SearchByName
End Function
String concatenation is BAD. Next thing you know, Bobby Tables wrecks my system.
Now, the correct way to do this is to to make a proper oracle variable, by putting :searchterm in the string and adding the following line:
cmd.Parameters.Add(New OracleParameter("SEARCHTERM", searchterm))
The problem is since I am using a like statement, I need to be able to have % on either side of the search word, and I can't seem to do that with '%:searchterm%', it just gives an error of ORA-01036: illegal variable name/number.
Can I parameterize but still have my flexible like statement be a part of it?

Instead of doing the concatenation in your VB code, do the concatenation in the SQL statement. Then what you're trying to do should work. Here's some SQL illustrating what I'm talking about:
select ID_ELEMENT, ELEMENT_NAME
from table_of_elements
where upper(ELEMENT_NAME) like ('%' || upper(:searchterm) || '%')
BTW, you might end up with more efficient queries if you switch the collation on ELEMENT_NAME to case-insensitive and then remove the calls to upper().

Since you're using oracle, another option would be to use Oracle Text to perform the search.
It can take a bit to set up properly, but if you have a large amount of text to search, or have some sort of structured data, it can offer you many more options than a simple wild-card comparison.
It also has some nice features for dealing with multiple languages, if you happen to have that problem as well.

Related

Stored Procedure from SQL Server is not filling ASP.Net dropdown using VB.Net coding

Situation:
I have a pre-built form that uses two Form Views. One is for pickup and one for delivery. The form can't be changed due to client requirements. The entire form and Form Views use a WizardStep process.
Each side pre-fills in the clients company name and related data such as Pickup Name, address, city, state etc as well as Delivery Info all using the same fields using the same stored procedure.
The stored procedure shows the concat of Name and Address 1 as FullName and the ClientSubCtr. ClientSubCtr is an int field. This id is what populates the correct data to each dropdown.
The dropdown list is not inside the FormView and falls under the form tag.
When testing the connection via the <asp:SqlDataSource> tag everything works in the Configure Data Source and data is returned using the appropriate parameter. The connection for the matching client data uses a basic Select to the table used in the stored procedure. I've tried to use a different stored procedure for the data, but no success there either. I am using SQL Server Express right now locally.
I've tried disabling the asp:SqlDataSource and just use a database connection from page_load, which did not work. I've commented it out as I'm still trying different things. I may be missing some syntax to connect to the dropdown here.
'This did not work
'Using conn As New SqlConnection(connect)
' strSQL = "SELECT ClientSubCtr,CONCAT(Name,Address1) AS FullName 'FROM tblClientsSub"
' strSQL = strSQL & " WHERE Account ='" & sessAccount & "'"
' strSQL = strSQL & " ORDER BY FullName ASC"
' conn.Open()
' Using cmd As New SqlCommand(strSQL, conn)
' cmd.Parameters.AddWithValue("#Account", sessAccount)
' cmd.CommandText = strSQL
' cmd.ExecuteNonQuery()
' Dim dr As SqlDataReader = cmd.ExecuteReader()
' If dr.HasRows Then
' Do While (dr.Read())
' ClientSubCtr = dr.GetValue(0)
' FullName = dr.GetValue(1)
' s1_cboAcct.DataTextField = FullName
' s1_cboAcct.DataValueField = ClientSubCtr
' s1_cboAcct.DataBind()
'Corrected Databind still does not work
' Loop
' Else
' dr.Close()
' End If
' End Using
'End Using
Additional info: Prior to using this I have two Pre-Render functions. One for Pickup and Delivery which fills in what my client calls a Docket. It shows the info that was pre-filled in with the initial loading or by the Selected Change on the dropdown and or where the client manually added or changed something, thus sending the correct information to the database for eventual download.
Note: This was originally written making OLEDB ACE connections to my clients Go Daddy account. Go Daddy moved the site to a new server and will no longer support ACE 12.0. We begged for them to change this. Thus, the move to SQL Server.
When I get what I believe is the data getting load I get an error in my pre-render on this tag. I've purposely commented out all the pre-render to see what was returned in regards to errors. No errors are return and the general form returns showing the drop-down but no data and the field in the FormView can't be seen. In other words not even the labels are showing. I've checked all asp:sqldatasource settings.
' If IsNothing("txtcboAcct.Text") Then
'lblDocketPUName.Text = ""
(I've tried using Request.form here, but data isn't loaded so I get nothing.) and commented out the If Else End If
' Else
' lblDocketPUName.Text = CType(frmViewPUClient.FindControl("txtcboAcct"), TextBox).Text
(This is where I get an error where the object is not found because neither the data for the dropdown is loading or the data for the forms.)
' End If
I know this is a lot to digest, but I'm totally baffled why this works perfectly using OLEDB and Access 2007 but when switching to SQL Server and the exact tables named the same, it does not. Any insight to why the stored procedure won't fill in the dropdown or why even my code in page_load wouldn't fill in the drop-down would be helpful. I've updated all parameters to use the # symbol.
Here is the stored procedure:
'ALTER PROCEDURE [dbo].[qryFullNameAddr]
'#Account nvarchar(10) OUTPUT
'AS
' -- Add the parameters for the stored procedure here
'DECLARE #ClientSubCtr int
'DECLARE #Name nvarchar(50)
'DECLARE #Address1 nvarchar(125)
'DECLARE #FullName nvarchar(255)
'BEGIN
' -- SET NOCOUNT ON added to prevent extra result sets from
' -- interfering with SELECT statements.
' SET NOCOUNT ON;
' /*Write statements for procedure here */
'SELECT ClientSubCtr,CONCAT(Name,Address1) AS FullName
'FROM tblClientsSub
'WHERE #Account=#Account
'ORDER BY FULLNAME, ADDRESS1 ASC
'end
Well, first I would back the truck up so to speak.
I would create a blank new test web page. Put in a dropdown list, and get it working.
Next up:
You don't need to loop for a drop down list. You can feed a dropdown a TABLE. And that table can be a query, or perhaps a stored procedure.
So, for starters, lets display a Hotel List in a drop down. And like most drop downs, there are often two columns the hidden "id" or often "PK" of the row, and then the display text.
So, our blank we page, we drop in a drop down list. Say like this:
<form id="form1" runat="server">
<div>
<asp:DropDownList ID="DropDownList1" runat="server"
DataTextField="HotelName"
DataValueField="ID" Height="22px" Width="219px">
</asp:DropDownList>
</div>
</form>
And now the code to fill - and as noted ALWAYS ALWAYS load only on first page load - that is PostBack = false - (break that rule, and you can't really even make a correctly working web page).
Ok, so our code is now this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadData
End If
End Sub
Sub LoadData()
Using con As New SqlConnection(My.Settings.TEST4)
Dim strSQL As String = "SELECT ID, HotelName from tblHotels ORDER BY HotelName"
Using cmdSQL As SqlCommand = New SqlCommand(strSQL, con)
con.Open()
Dim rstData As New DataTable
rstData.Load(cmdSQL.ExecuteReader)
DropDownList1.DataSource = rstData
DropDownList1.DataBind()
DropDownList1.Items.Insert(0, New ListItem("", "")) ' optional blank starting select
End Using
End Using
End Sub
That's it!!!
Also, don't know why so many code examples set the DataTextField and DataValue field in code? Why???? Just use the property sheet - little need to put such things in the code side - ESPECIALLY when the property sheet can do that for you!!!
Next up? Well, everyone always on SO piles in about how you should use parameters in such code in place of concatemer strings. This is correct, but if you code with parameters, then you can OFTEN reduce code, and CLEAN UP your sql anyway.
In place of say a messy sql string, you remove the text ('quotes'), and numbers (no quotes), and this even works better for dates.
So this benefit is near as great as the issue of sql injection issues.
(and to be fair, you noted a stored procedure, and it not working, so clearly you decided to try anything here - I get that approach).
So, your code snip? I would code it this way:
Dim strSQL As String =
"SELECT ClientSubCtr,CONCAT(Name, Address1) AS FullName FROM tblClientsSub " &
"WHERE Account = #Acc " &
"ORDER BY FullName ASC"
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand(strSQL, conn)
cmdSQL.Parameters.Add("#Acc", SqlDbType.NVarChar).Value = sessAccount
conn.Open()
s1_cboAcct.DataSource = cmdSQL.ExecuteReader
s1_cboAcct.DataBind()
End Using
End Using
Now is that not MUCH more readable?
And in fact cut + paste your code?
I noticed this:
SELECT ClientSubCtr,CONCAT(Name,Address1) AS FullName 'FROM
Wait? what is the ' right before the FROM???? You have a stray ', don't you?
Maybe that was a code cut + paste error, maybe not. But the REAL POINT?
By using parmaters we go nuclear on the single, double quotes, and all that jazz.
in other words, sure, avoiding string concntatiion of values is important for reasons of sql injection, but REALLY MUCH more valuable is how we don't have to bother with all those quotes and stuff. The result is it is MUCH HARDER to miss or mess up!!!
The other issue of course is by using the parameters as per above?
Well, we get intel-sense. And if I have several more, then in VS I just hit ctrl-d to dupplie the line. I actually wind up TYPING LESS code then If I attempted to introduce those values right into the sql!!! Imagine that - eaiser to read, and even less typing of code!!! - all this amounts up to much less error prone code. I mean, one missing quote like ' or a extra one? You in trouble and will waste time!!
You ALSO note that I did not even use a reader into a DataTable, and just shoved the reader results RIGHT into the combo box - saves even more code!!!!
However, while a dropdown, listview, gridview (and many more) can ALL accept DataSource as a cmdSQL.ExecuteReader as I did?
I still STRONG recommend you use my first version for Grids/list views etc. (and a HUGE reason exists for this!!!). (data pagers don't work if you feed a gridview a reader, but a table they work fine, and also the data bind events (not often used for combo box, but VERY often used for grids often needs the data source - so for this combo? Sure you can skimp out on the two extra lines of code (to create a DataTable and load it). But for a gridview, don't use that cool short cut I used above by feeding the reader directly into grids or listviews - but for combos, perfect fine.
So, with above, and after running above code, we have this:
As noted, if you DO test or decide to load up a combo box, and NOT use say a DataSoruce control dropped into the markup (I don't like those things all that much), then MAKE SURE you put and ONLY call the loading of such controls inside the If Not IsPostBack code stub. DO NOT EVER fail to follow this rule.
Again: do NOT EVER EVER fail to follow this rule.
Why?
Becuase any other button or any other thing you EVER will place on that page may well need to do a post-back (say even a few simple buttons). So, if you say select a combo box, fill out some other things, and have some button (and a button click event with code behind)?
Wel, keep in mind that page load ALWAYS FIRES each post-back!!!!
So, if your code re-loads say the combo box, then it going to blow out any existing selecting the user has for that combo (or grid or just about anything else!!!). So, you have to write your code in a way that it will "assume" and "work" and "survive" post-backs. So your loading up of controls thus should only be ONE time, and on the first page load - hence my HUGE LONG narrative of how vast important it is to follow this simple rule.

how to add wild card?

Hi i am new programmer asp.net and vb.net. I wanted using wild card to search engine.
i am puzzled put wild card (%).
Protected Sub search()
Dim ds As New DataSet
Dim cls As New Class1
ds = cls.returndataset("select * from student where Nama_Depan like '" & nama.Text & "' ")
viewatt.DataSource = ds
viewatt.DataBind()
End Sub
Thanks in advance for any help, and I'm really sorry if this has been asked before.
As others have already pointed out
select * from student where Nama_Depan like '%" & nama.Text & "%'
is the correct way to use wildcards
Here is a link to the W3Schools tutorial on SQL wildcards: http://www.w3schools.com/sql/sql_wildcards.asp
I would suggest you have a quick read through, the W3schools articles are usually really good.
This however is a really bad way to execute SQL commands from your code, someone could quite easily inject some SQL code into your textbox and when your query is executed it could destroy your database.
As an exaple if someone entered '; DROP TABLE * -- into your textbox, this would allow your first query to run, then it would drop all your tables.
It is really easy to get around this by using parameterised queries or stored procedures. Personally I would favour using Stored Procedured and you can do so like this.
CREATE PROCEDURE MyProc
(
#param1 VARCHAR(50)
)
AS
BEGIN
select * from student where Nama_Depan like '%"'+#param1+'%'
END
You would then just need to edit your VB code to call the stored procedure rather than executing the SQL command.
The other advantage of using a stored procedure is that they perform slightly faster and are more efficient, this is because when you pass some SQL to the database it has to compile it into a sql command to execute, stored procedures are already stored on the database and so do not need to do this.
There are also some added security benefits to using a stored procedure rather than just transmitting a SQL statement, If anyone were to intercept your SQL statement on its way across the network / internet they could gain some insight into your database structure. With a stored procedure all they could possibly intercept is the procedure name and some parameters.
Try this.
Protected Sub search()
Dim ds As New DataSet
Dim cls As New Class1
ds = cls.returndataset("select * from student where Nama_Depan like '%" & nama.Text & "%' ")
viewatt.DataSource = ds
viewatt.DataBind()
End Sub

How to get around the ' (quote) problem when saving text using SQL

I've a textbox that I save to a SQL Database using ASP.NET C#.
The problem is using quotes, for instance with words like he's and it's. When you save it, I get an SQL error.
From experience to get around the issue, I use to use replace command to find all occurances of quotes and replace them with another character. Then if I was to read the database and the text that was previously saved, I'd replace again.
Is there a better of doing this? Or do you still have to use this 'old' way.
Use parametrized queries.
Using replace is dangerous and can lead to unintentional SQL injection vulnerabilities.
Always use parameterized queries. Never concatenate strings together (with un-sanitised input information) to create SQL on the fly. You run the risk of being vulnerable to SQL-injection attacks.
Read the following guide carefully:
http://www.csharp-station.com/Tutorials/AdoDotNet/Lesson06.aspx
And when you understand the following comic strip, you are ready to code:
http://xkcd.com/327/
As the other answers point out, the correct answer here is to use parameterized queries.
However, this is sometimes not possible. In these cases, you're better off doubling the quotes:
String str = "he's";
str = str.Replace("'", "''");
There's two ways to get around this issue:
Use a parameterised query, e.g. in http://www.csharp-station.com/Tutorials/AdoDotNet/Lesson06.aspx - this will also help avoid basic SQL injection if you're worried about that
Create 2 new functions, one to double up every apostrophe, so that it's saved into the database correctly, and another to remove extra apostrophes when retrieving data from the database.
I would recommend the first method as it's a lot cleaner (and safer) in my opinion. If you're using C# for your ASP.NET you can do things like in my below example:
string clientName = "Test client";
SqlCommand sqlCommand = new SqlCommand("SELECT COUNT(*) FROM Client WHERE UPPER(ClientName) = UPPER(#ClientName)", connection);
sqlCommand.Parameters.AddWithValue("#ClientName", clientName);

LINQ-to-SQL Query Returning No Results

I have a query using LINQ-to-SQL. It queries an underlying database table Rooms. It uses Where conditions to narrow down the results, namely:
Gender.
Current Occupancy < Max Occupancy
Available Flag is Checked
I know this should return results but it keeps returning an empty set. Any ideas? Code is below
Dim selectedHalls = (From sh In dbHalls.Rooms _
Where sh.gender = Session("gender").ToString _
Where sh.max_occupancy > sh.current_occupancy _
Where sh.is_available = 1 _
Select sh.building_name).Distinct()
UPDATE: I've verified that the issue is with the statement where sh.is_available = 1, which doesn't make sense since this is a bit field.
I think the best way to find out what the problem is to generate the SQL string and test your assumption that "it should return results" by executing it directly against the data source.
To do this:
Dim sqlQuery As String = dbHalls.GetCommand(
(From sh In dbHalls.Rooms _
Where sh.gender = Session("gender").ToString _
Where sh.max_occupancy > sh.current_occupancy _
Where sh.is_available = 1 _
Select sh.building_name).Distinct()
).CommandText
(I usually use C#, but I think that's how you declare a string in VB, right?)
Anyway, this will give you an SQL statement that will be more informative than anything we can give you without being able to look at your underlying database.
The only thing that jumps out at me as being potentially problematic is the Session("gender"). You are basically relying on your Session object to be populated, have a value for the case-sensitive string key "gender" that matches the case-sensitive string field gender in your database. This sounds like quite a few assumptions, that may or may not be tested and could be the reason for receiving empty results.
EDIT
I just saw your update. Linq-to-sql, interprets a bit field as a boolean value, not an integer value. Try changing it to just where sh.is_available
I don't see anything obviously wrong with your LINQ, so try removing the Where clauses and bringing them back one at a time. That should let you know what's causing the issue, though I agree with Andrew Vogel that the gender clause is a likely suspect.
If the query comes back empty after all the Where clauses are gone, then you'll know there's something else going on here.
Do you really record their genders as "Female" and "Male" in dbHalls.Rooms.sh?

Integer variable is acquiring a string value of "ad" somewhere along the line, can anyone see where?

Here is my code:
I should get output of the department id (did) as an integer and the templatefilename (result) that is required.
The errors I get are: Conversion from string "ad" to type 'Integer' is not valid. I'm fairly new to asp.net and cannot see where the did variable picks up the "ad" string.
Any help would be greatly appreciated.
Thanks
When you construct the query to the table departmentsgroupings, you're changing the value of sql, but you aren't creating a new SqlCommand. This means that cmd still contains the old SQL statement (the query to the Modules table) which, when executed, returns "ad".
To fix this, change your code as follows:
sql = ("select departmentsid from departmentsgroupings where groupingid =" & pageid & "")
Set cmd = New SqlCommand(sql, conn)
did = (cmd.ExecuteScalar)
You may have expected the change you made to sql to get passed on automatically to the SqlCommand -- but it doesn't work that way.
Edit: Your code, as written, is vulnerable to SQL injection attacks. If you don't know what these are, you need to read the first answer to this:
How does the SQL injection from the "Bobby Tables" XKCD comic work?
To protect yourself against these kinds of attacks, use parameterized queries.
The mistake is in these lines:
sql = ("select departmentsid from departmentsgroupings where groupingid =" & pageid & "")
did = (cmd.ExecuteScalar) <---- Wrong command executed here.
You presumably meant to execute the code in sql, not cmd again.

Resources