Subquery returned more than 1 value - asp.net

I know this topic is all over the place, but I am not doing an INSERT, UPDATE, DELETE. My statement is a plain and simple SELECT statement and so far has worked with 116 different items in my database until I got to one.
I have a search engine and am going through every single product in our database to add information to it. This is all done through the website, but when I search for ProductID 331 and click on it, it goes to the error page that says Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
It doesn't make any sense to me that the website would error with only this one product.
This is the statement I am using. Does anyone know why 1 product would be causing this error?
WebService:
Public Class ProductSearch
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function GetProducts(ByVal prefixText As String, ByVal count As Integer)
As String()
Dim ProductSql As String = "Select DISTINCT ProductID, ProductName
FROM Product WHERE ProductName
LIKE '%' & #prefixText & '%'
ORDER BY ProductName ASC"
Using sqlConn As New SqlConnection
(System.Configuration.ConfigurationManager.ConnectionStrings
("LocalSqlServer").ConnectionString)
sqlConn.Open()
Dim myCommand As New SqlCommand(ProductSql, sqlConn)
myCommand.Parameters.Add("#prefixText", SqlDbType.VarChar, 50)
.Value = prefixText
Dim myReader As SqlDataReader = myCommand.ExecuteReader()
Dim myTable As New DataTable
myTable.TableName = "ProductSearch"
myTable.Load(myReader)
sqlConn.Close()
Dim items As String() = New String(myTable.Rows.Count - 1) {}
Dim i As Integer = 0
For Each dr As DataRow In myTable.Rows
Dim id As String = dr("ProductID").ToString()
Dim name As String = dr("ProductName").ToString()
Dim item As String = AjaxControlToolkit.AutoCompleteExtender
.CreateAutoCompleteItem(name, id)
items.SetValue(item, i)
i += 1
Next
Return items
End Using
End Function
End Class
The aspx page that calls the webservice:
<%# Page Title="Product Search" Language="VB" MasterPageFile="~/MasterPage.master"
AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="Default" %>
<%# Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit"
TagPrefix="asp" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
<script type="text/javascript">
function AutoCompleteClientMethod(source, eventArgs) {
var value = eventArgs.get_value();
window.location = ("/Product/Default.aspx?id=" + value)
}
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="body" Runat="Server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="ProductSearch.asmx" />
</Services>
</asp:ScriptManager>
<asp:TextBox ID="Search" runat="server" AutoComplete="off"></asp:TextBox>
<asp:AutoCompleteExtender ID="AutoCompleteExtender1" runat="server"
TargetControlID="Search" ServicePath="~/ProductSearch.asmx"
ServiceMethod="GetProducts" MinimumPrefixLength="1" CompletionSetCount="120"
EnableCaching="true" OnClientItemSelected="AutoCompleteClientMethod">
</asp:AutoCompleteExtender>
</div><!--End of main div -->
</asp:Content>
UPDATE: 11/9/2011 - I have found a couple more records that have this problem. They are ProductID 331-335. I have no idea what is going on here. Could it be that those products don't really exist or that they have some kind of bug?
Here is a list of ProductIDs and their corresponding ProductNames that have this error:
122 'Managed account section of the Web Site'
331 'Elliott Wave Principle Key to Market Behavior'
332 'Targeting Profitable Entry & Exit Points'
333 'Essentials of Trading It's not WHAT You Think, It's HOW You Think'
334 'Exceptional Trading The Mind Game'
335 'Fibonacci Analysis'

I assume this is the sub-select query, DISTINCT doesn't mean one result. You can use TOP 1 to guarantee one result, but it doesn't guarantee it is the one you want.
Select TOP 1 DISTINCT ProductID, ProductName
FROM Product WHERE ProductName
LIKE '%" & prefixText & "%'
ORDER BY ProductName ASC

Besides Rick's answer, I would add that you should never concatenate strings to form SQL statements. Use parametrized queries instead. String concatenation exposes you to SQL Injection attacks. Also, by using parametrized queries you may gain performance if the query plans can be reused.
See this other StackOverflow post for a good discussion regarding parametrized queries on VB.NET.

I figured out what the problem is. For some reason, these problematic products have more than one value assigned to them in data fields that SHOULD have only one item. The database has been changed recently so that doesn't happen, but I guess these 5 products were already messed up and have now been found out.
Thanks for all the help guys! I wish I would've thought to check further into the database sooner. (There are about 15 tables, so it's usually what I think of to do last)

Related

Load a set of variables from a SQL database - language ASP.NET VB

I am trying to translate an old "Classic ASP" page to ASP.NET page. I have limited ASP.NET knowledge.
I am reading some data from a SQL server.
I have a basic question, but so far I was not able to successfully implement any working solution.
The code attached is working well; shows the data I need.
I would like to have a similar code structure but this time I would like to assign the data from SQL to variables (I do not need to print these output values on the screen):
title (output of SQL query) ---> "titleV" variable (string)
father (output of SQL query) ---> "fatherV" variable (string)
age (output of SQL query) ---> "ageV" variable (number)
If possible, please suggest to me a piece of code I can test.
Thank you.
<%# Import Namespace="System.Data.OleDb" %>
<script runat="server">
sub Page_Load
dim dbconn,sql,dbcomm,dbread
dbconn=NewOleDbConnection("connection string to sql server")
dbconn.Open()
sql="SELECT * FROM photos"
dbcomm=New OleDbCommand(sql,dbconn)
dbread=dbcomm.ExecuteReader()
customers.DataSource=dbread
customers.DataBind()
dbread.Close()
dbconn.Close()
end sub
</script>
<!DOCTYPE html>
<html>
<body>
<form runat="server">
<asp:DataList id="customers" runat="server">
<ItemTemplate>
<%#Container.DataItem("title")%> in
<%#Container.DataItem("father")%> with
<%#Container.DataItem("age")%> years
</ItemTemplate>
</asp:DataList>
</form>
</body></html>
You can save one record in above 3 variables because you are not using array type or collections. You can assign the data from the table to your variables using the following code.
sub page_load()
dbconn = new oledbconnection("conn str")
dbconn.Open()
sql="SELECT * FROM photos"
dim adpt as OledbDataAdapter
dim rs as new DataTable
adpt = new OledbDataAdapter(sql,dbconn)
adpt.fill(rs)
customers.DataSource = rs
customers.DataBind()
dim titleV, fatherV As String
dim ageV as integer
titleV = rs.Rows(0).Item(0) 'Rows(0).Item(0) gives you first row's first field value.
fatherV = rs.Rows(0).Item(1)
ageV = rs.Rows(0).Item(2)
dbconn.Close()
end sub
</script>
<!DOCTYPE html>
<html>
<body>
<form runat="server">
<asp:DataList id="customers" runat="server">
<ItemTemplate>
<%#Container.DataItem("title")%> in
<%#Container.DataItem("father")%> with
<%#Container.DataItem("age")%> years
</ItemTemplate>
</asp:DataList>
</form>
</body></html>

ajax control toolkit fileupload

I am trying to get the fileupload control from the ajax control toolkit to work.
I need to use the uploaded files in my code behind (I use asp.net),
This includes unzipping, resizing and putting some data in a database.
The problem I have is that when I use ajaxUpload1_OnUploadComplete, i can't get the text from a textbox on the same page.
When I use a breakpoint, I noticed that the value of the textbox is just "".
I have searched a lot and I really can't find a solution for this, so I hoped that someone here might be able to help.
I have pasted my code below, thanks in advance!
.aspx code:
<%# Page Title="" Language="VB" MasterPageFile="~/MasterPage.master" AutoEventWireup="false"
CodeFile="Upload.aspx.vb" Inherits="_Default" %>
<%# Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<asp:Label ID="LblUploadError" runat="server" Text="Please login first" Visible="false"></asp:Label>
<asp:Panel ID="PnlUpload" runat="server" Visible="false">
<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server"></asp:ToolkitScriptManager>
<span>Album name:</span><br />
<asp:TextBox ID="txtAlbumNaam" runat="server" ViewStateMode="Disabled"></asp:TextBox><br />
<span>Private album</span> <asp:CheckBox ID="chkPrivate" runat="server" /><br />
<span>Upload files (.zip, .jpg, .jpeg or .png)</span><br />
<asp:AjaxFileUpload id="ajaxUpload1" OnUploadComplete="ajaxUpload1_OnUploadComplete" ThrobberID="MyThrobber" runat="server" AllowedFileTypes="jpg,jpeg,zip,png" /><br />
<asp:Label ID="lblError" runat="server"/><br />
</asp:Panel>
</asp:Content>
code behind:
Imports Ionic.Zip
Imports System.IO
Imports System.Data.OleDb
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
If Session("LoggedIn") = True Then
PnlUpload.Visible = True
Else
LblUploadError.Visible = True
End If
End Sub
Protected Sub ajaxUpload1_OnUploadComplete(ByVal sender As Object, ByVal e As AjaxControlToolkit.AjaxFileUploadEventArgs)
'indien zip:
Dim ziperror As Boolean = False
If System.IO.Path.GetExtension(e.FileName) = ".zip" Then
ajaxUpload1.SaveAs(Server.MapPath("./TempZips/" & e.FileName.ToString))
Dim ZipToUnpack As String = Server.MapPath("./TempZips/" & e.FileName.ToString)
Dim UnpackDirectory As String = Server.MapPath("./TempFotos")
Using zip1 As ZipFile = ZipFile.Read(ZipToUnpack)
Dim file As ZipEntry
For Each file In zip1
Dim strExtensie As String = System.IO.Path.GetExtension(file.ToString)
If Not (strExtensie = ".jpeg" Or strExtensie = ".jpg" Or strExtensie = ".png") Then
ziperror = True
lblError.Text = "The .zip structure is incorrect, please make sure that you only have compatible pictures inside the zip file."
End If
Next
If ziperror = False Then
For Each file In zip1
file.Extract(UnpackDirectory, ExtractExistingFileAction.OverwriteSilently)
Next
End If
End Using
'indien foto:
ElseIf System.IO.Path.GetExtension(e.FileName) = ".jpeg" Or System.IO.Path.GetExtension(e.FileName) = ".jpg" Or System.IO.Path.GetExtension(e.FileName) = ".png" Then
ajaxUpload1.SaveAs(Server.MapPath("./TempFotos/" & e.FileName.ToString))
Else
'indien geen van beide
lblError.Text = "Invalid filetype on one of the files, please use other files."
End If
'tempzips leegmaken
If ziperror = False Then
For Each foundFile As String In My.Computer.FileSystem.GetFiles(Server.MapPath("TempZips"))
File.Delete(foundFile)
Next
'verkleinen en album toevoegen aan database:
Dim strFolderDirectory As String = Server.MapPath("users/" & Session("UserNickName") & "/" & txtAlbumNaam.Text)
System.IO.Directory.CreateDirectory(strFolderDirectory)
Dim strDirectory As String = Server.MapPath("TempFotos")
Dim intAantalFotos As Integer = 0
For Each foundFile As String In My.Computer.FileSystem.GetFiles(strDirectory)
Using Afbeelding As System.Drawing.Image = System.Drawing.Image.FromFile(foundFile)
Dim resizedimage As System.Drawing.Image
Dim resizedwidth As Integer
resizedwidth = (300 / Afbeelding.Height) * Afbeelding.Width
resizedimage = Afbeelding.GetThumbnailImage(resizedwidth, 300, Nothing, New IntPtr)
resizedimage.Save(strFolderDirectory & "/" & Path.GetFileName(foundFile))
End Using
intAantalFotos += 1
Next
Dim CmdInsert As New OleDbCommand
Dim Sqlstatement As String = "INSERT INTO tblAlbums (userID, createdDate, pictures, private, albumName) VALUES (#userID, Now(), #pictures, #private, #albumName);"
CmdInsert.Connection = dbConn.cn
CmdInsert.CommandText = Sqlstatement
CmdInsert.Parameters.AddWithValue("userID", CInt(Session("userID")))
CmdInsert.Parameters.AddWithValue("pictures", intAantalFotos)
CmdInsert.Parameters.AddWithValue("private", chkPrivate.Checked)
CmdInsert.Parameters.AddWithValue("albumName", txtAlbumNaam.Text)
dbConn.cn.Close()
dbConn.cn.Open()
CmdInsert.ExecuteNonQuery()
dbConn.cn.Close()
'TempFotos leegmaken
For Each foundFile As String In My.Computer.FileSystem.GetFiles(strDirectory)
File.Delete(foundFile)
Next
'pagina herladen
LblUploadError.Visible = True
LblUploadError.Text = "Your pictures have been successfully uploaded!"
End If
End Sub
End Class
The problem is that ajax control toolkit file upload control can upload files to server using hidden iFrame (depending on what HTML5 features browser supports). Hidden iFrame references to the same url as you page and that's why you page loaded once more but only to hidden iframe. That's why on server side during handling UploadComplete event you get that the text box has empty value (because it is really have empty value because this is the state of the page which was loaded in iframe).
One of the solution to your problem is performing additional logic (which depends on entered data) after uploading is complete. To do this you can handle client side upload complete event and perform postback (or ajax request) manually.
Other solution can be setting content of the hidden iFrame elements manually before upload will start. In this case you can get hidden iframe, found necessary HTML elements (like text input in your case) and set it value the same as it was entered by user. But this approach can required extending logic of the ajax control toolkit upload control on client side.

ASP.Net AutoCompleteExtender VB WebMethod not firing - why?

Definitely at my wits end here. This should be simple. In a page to create new user accounts, we have a database with a little of allowable users. To streamline getting the Email address of the new user correct, we want to use an AutoComplete extended textbox.
Now I know that WebMethods are working because I have a cascading-drop-down tied to web methods in another page.
As I'm just starting on this page, the code is simple.
The page itself:
<cc1:ToolkitScriptManager ID="ScriptManager2" runat="server"/>
<p></p> Please enter new user's Email:
<asp:TextBox ID="txtUser" runat="server" />
<cc1:AutoCompleteExtender runat="server" ID="autUser" TargetControlID="txtUser"
ServiceMethod="ScanGALUsers" ServicePath="~/AutoScan.asmx"
MinimumPrefixLength="3" CompletionSetCount="150" /> <p></p>
The .asmx file is simple:
<%# WebService Language="VB" CodeBehind="~/App_Code/VB_Code/AutoScan.vb" Class="AutoScan" %>
The WebMethod:
<System.Web.Script.Services.ScriptService()> _
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class AutoScan
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Shared Function ScanGALUsers(ByVal strPrefix As String, ByVal intMaxCount As Integer) As String()
Dim arlResults As New ArrayList
Dim intCount As Integer
Dim dt As DataTable
Dim colParameters As New SortedList
SysDA.LogDebug("ScanGALUsers called with parameters: " & strPrefix & " and count of " & intMaxCount.ToString)
... Deleted for brevity ...
If intCount > 0 Then
Dim arrResults(intCount - 1) As String
arrResults = arlResults.ToArray(GetType(System.String))
Return arrResults
Else
Return Nothing
End If
End Function
End Class
I'm not even getting to the LogDebug statement. I've used all the same boilerplate code (Inherits, the 'WebService' tags, etc) that worked in the other WebMethod with the appropriate changes to the Class name but this really has me stumped.
What am I missing that I'm not even making it to the method?
Did you ever resolve this issue? Have you tried removing Shared from your WebService declaration? This has worked for me before (and I don't know why!).

asp.net vb listview concatenate data items in code behind

I have a listview with some data bound to it.
In this data are column for an address.
How would I go about accessing these data items in code behind so I can concatenate them into one easy variable and miss out columns that have no data in, i have fields:
address
address1
town
county
postcode
I don't have a problem with the concatenation just accessing the data items.
Thanks J.
UPDATED
I am getting data out via a dataset and binding it to a listview.
Is it possible to access data items in the code behind to format or do whatever i want with them then showing it in the list view such as, concatenating the address fields into one variable?
so instead of writing:
DataBinder.Eval(Container.DataItem, "address") & ", " & DataBinder.Eval(Container.DataItem, "address1") & ", " & DataBinder.Eval(Container.DataItem, "town") etc...
in the actual list view i could do this in the code behind in a string variable then show the variable in the list view?
'select command
Dim cmdSchedule As SqlCommand = New SqlCommand()
cmdSchedule.Connection = keypadSQL
cmdSchedule.CommandText = "spSchedule"
cmdSchedule.CommandType = CommandType.StoredProcedure
'data adapter
Dim daSchedule As SqlDataAdapter = New SqlDataAdapter
daSchedule.SelectCommand = cmdSchedule
'data set
Dim dsSchedule As DataSet = New DataSet()
daSchedule.Fill(dsSchedule, "Schedule")
lvSchedule.DataSource = dsSchedule
lvSchedule.DataBind()
cmdSchedule.Dispose()
First put your items into accessible controls in the ListView, such as a label or literal.
<asp:ListView ID="ListView1" runat="server">
<ItemTemplate>
<asp:Label ID="lblAddress" runat="server" Text="<%= Eval("address") %>" />
</ItemTemplate>
</asp:ListView>
Then you can loop through the items and using FindControl, pull each string out individually.
Dim items As List(Of ListViewDataItem) = ListView1.Items
For Each item As ListViewDataItem In items
Dim strAddress As String = CType(item.FindControl("lblAddress"), Label).Text
Next
UPDATED
I'd think the best way would be to format it in SQL Stored Procedure and return it as a new field. Something like this:
SELECT *, address + ', ' + address1 + ', ' + town ', ' + county + ', ' postcode AS fullAddress
FROM ...
Then you'd just have to use <%= DataBinder.Eval(Container.DataItem, "fullAddress") %> to get the formatted address. You could even format it with HTML in the SP as long as you're weary of potential injection attacks (not sure of the original address input method).

ASP.NET Load & Display DataGrid based on another control, when page loads

On my ASP.NET content page, I have a DropDownList and a DataGrid. The DropDownList is populated from the results of an SQL query when the page loads. I want the DataGrid to be populated from the results of an SQL Query, but that query depends on the selected item of the DropDownList. I have an event handler for DropDownList1.SelectedIndexChanged, which calls a function PopulateDG. This function gets the selected item from the DropDown (defaulting to the first element if SelectedItem is Nothing), creates an SQLDataAdapter, queries the database, fills the DataSet, and finally calls the DataGrid's DataBind(). This all works perfectly when the function is called from the DropDown's event handler.
I would now like to have the DataGrid appear when the page is first loaded, using the information from the first element of the DropDown (since it'd be before the user has selected anything). I have tried putting PopulateDG in the Init, PreInit, PreRender, Load, and Unload handlers for the Page and both controls, to no avail. When I use the debugger, I can see that the ItemsGrid.DataSource does indeed have the information I want, but ItemsGrid.DataBind does not seem to cause the Data Grid to appear on the page like it does when called from the SelectedIndexChanged handler. The control simply doesn't render onto the page.
PopulateDG:
Protected Sub PopulateDG()
Dim WeekId As Integer
Dim DDIdx As Integer
If DropDownList1.SelectedItem Is Nothing Then
DDIdx = 0
Else
DDIdx = DropDownList1.SelectedIndex
End If
WeekId = DropDownList1.Items.Item(DDIdx).Value
Dim myConnection As SqlConnection
Dim SQLCommand As SqlDataAdapter
Dim rootWebConfig As System.Configuration.Configuration
rootWebConfig = Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~/Web.config")
Dim connString As System.Configuration.ConnectionStringSettings
connString = rootWebConfig.ConnectionStrings.ConnectionStrings("ConnectionString")
If Not (connString.ConnectionString = Nothing) Then
Console.WriteLine("Northwind connection string = " & connString.ConnectionString)
Else
Console.WriteLine("No Northwind connection string")
End If
myConnection = New SqlConnection(connString.ConnectionString)
SQLCommand = New SqlDataAdapter("SELECT CONVERT(VARCHAR(10), WorkDate, 101) AS WorkDate, CONVERT(VARCHAR(10), WorkStartTime, 8) AS StartTime, CONVERT(VARCHAR(10), WorkEndTime, 8) AS EndTime, BreakMinutes FROM Days WHERE WeekId = " & WeekId & " ORDER BY WorkDate", myConnection)
Dim ds As DataSet = New DataSet()
SQLCommand.Fill(ds)
ItemsGrid.DataSource = ds
ItemsGrid.DataBind() 'DEBUGGER BREAK POINT HERE
End Sub
Content markup of my aspx page:
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" Runat="Server">
<asp:DropDownList ID="DropDownList1"
runat="server"
DataSourceID="TimeSheetDB"
DataTextField="WeekEndLabel"
DataValueField="WeekId"
OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged"
AutoPostBack="True">
</asp:DropDownList>
<asp:SqlDataSource ID="TimeSheetDB"
runat="server"
ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
SelectCommand="SELECT [WeekId], 'Week Ending ' + CONVERT(varchar(10), EndDate, 101) AS WeekEndLabel FROM [Weeks] ORDER BY [EndDate]">
</asp:SqlDataSource>
<asp:DataGrid id="ItemsGrid"
BorderColor="black"
BorderWidth="1"
CellPadding="3"
AutoGenerateColumns="true"
runat="server"
>
</asp:DataGrid>
</asp:Content>
At the second to last line of the sub, I have a break point, and using the debugger I can see that ItemsGrid.DataSource.Tables.List(0) is populated exactly as I'd like, but the DataBind function seems to do nothing when called from anywhere except the SelectedItemChanged event handler. How can I make it work correctly when the page first loads?
I figured out the problem. The call to PopulateDG has to come in the DropDown's PreRender event handler. My problem seems to have been that I was trying to use the function either too early (ie, before the items were loaded into the DropDown control) or too late (after ASP had already determined how the DataGrid should appear, I assume, so that calling DataBind at that point had no effect). Clearly, I need to read up more on the various execution phases of pages and controls.

Resources