I have a FormView where I pull data from one table (MS Access), and then insert it (plus more data) into another table. I'm having issues with the dates.
The first table has two date fields: date_submitted and date_updated. In some records, date_updated is blank. This causes me to get a data mismatch error when attempting to insert into the second table.
It might be because I'm databinding the date_updated field from the first table into a HiddenField on the FormView. It then takes the value from the HiddenField and attempts to insert it into the second table:
Dim hfDateRequestUpdated As HiddenField = FormView1.FindControl("hfDateRequestUpdated")
myDateRequestUpdated = hfDateRequestUpdated.Value
'... It then attempts to insert myDateRequestUpdated into the database.
It works when there is a value there, but apparently you can't insert nothing into a date/time field in Access. I suppose I could make a second insert statement that does not insert into date_updated (to use when there is no value indate_updated), but is that the only way to do it? Seems like there should be an easier/less redundant way.
EDIT:
Okay. So I've tried inserting SqlDateTime.Null, Nothing, and DBNull.Value. SqlDateTime.Null results in the value 1/1/1900 being inserted into the database. "Nothing" causes it to insert 1/1/2001. And if I try to use DBNull.Value, it tells me that it cannot be converted to a string, so maybe I didn't do something quite right there. At any rate, I was hoping that if there was nothing to insert that the field in Access would remain blank, but it seems that it has to fill it with something...
EDIT:
I got DBNull.Value to work, and it does insert a completely blank value. So this is my final working code:
Dim hfDateRequestUpdated As HiddenField = FormView1.FindControl("hfDateRequestUpdated")
Dim myDateRequestUpdated = Nothing
If hfDateRequestUpdated.Value = Nothing Then
myDateRequestUpdated = DBNull.Value
Else
myDateRequestUpdated = DateTime.Parse(hfDateRequestUpdated.Value)
End If
Thanks everyone!
Sara, have you tried casting the date/time before you update it? The data mismatch error likely comes from the fact that the hfDateRequestUpdated.Value you're trying to insert into the database doesn't match the column type.
Try stepping through your code and seeing what the type of that value is. If you find that it's a string (which it seems it might be, since it's coming from a field on a form), then you will need a check first to see if that field is the empty string (VBNullString). If so, you will want to change the value you're inserting into the database to DBNull, which you can get in VB.Net using DBNull.Value.
We can't see your code, so we don't know exactly how you get the value into the database, but it would look something like this
If theDateValueBeingInserted is Nothing Then
theDateValueBeingInserted = DBNull.Value
EndIf
Keep in mind that the above test only works if the value you get from the HiddenField is a string, which I believe it is according to the documentation. That's probably where all this trouble you're having is coming from. You're implicitly converting your date/time values to a string (which is easy), but implicitly converting them back isn't so easy, especially if the initial value was a DBNull
aside
I think what Marshall was trying to suggest was the equivalent of the above code, but in a shortcut expression called the 'ternary operator', which looks like this in VB.Net:
newValue = IF(oldValue is Nothing ? DBNull.Value : oldValue)
I wouldn't recommend it though, since it's confusing to new programmers, and the syntax changed in 2008 from IFF(condition ? trueResult : falseResult)
Your code
Dim myDateRequestUpdated As DateTime
myDateRequestUpdated = DateTime.Parse(hfDateRequestUpdated.Value) : DBNull.Value()
has a couple of problems:
When you declare myDateRequestUpdated to be DateTime, you can't put a DbNull.Value in it.
I'm not sure you need the () for DbNull.Value: it's a property, not a method (I don't know enough VB to say for sure)
VB doesn't know that : operator
What you probably want is a Nullable(Of DateTime) to store a DateTime value that can also be missing.
Then use something like this to store the value:
myDateRequestUpdated = If(String.IsNullOrWhiteSpace(hfDateRequestUpdated.Value),
Nothing, DateTime.Parse(hfDateRequestUpdated.Value))
If hfDateRequestUpdated.Value is empty, then use Nothing as the result; else parse the value as date (which might fail if it is not a valid date!).
Try this:
Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim str As String
If TextBox1.Text.Length <> 0 Then
str = "'" & TextBox1.Text & "'"
Else
str = "NULL"
End If
sql = "insert into test(test1) values(" & str & ")"
dsave_sql(sql)
End Sub
Function save_sql(ByVal strsql As String, Optional ByVal msg As String = "Record Saved Sucessfully") As String
Dim sqlcon As New SqlConnection(strConn)
Dim comm As New SqlCommand(strsql, sqlcon)
Dim i As Integer
Try
sqlcon.Open()
i = CType(comm.ExecuteScalar(), Integer)
save_sql = msg
Catch ex As Exception
save_sql = ex.Message
End Try
sqlcon.Close()
Return i
End Function
Related
I have a query called qryAlloc_Source that has two paramaters under one criteria:
>=[forms]![frmReportingMain]![txtAllocStart] And <=[forms]![frmReportingMain]![txtAllocEnd])
A have a separate query that ultimately references qryAlloc_Source (there are a couple queries in between), and that query runs fine when I double click it in the UI, but if I try to open it in VBA, I get an error. My code is:
Dim rst As Recordset
Set rst = CurrentDb.OpenRecordset("qryAlloc_Debits")
I am getting run-time error 3061, Too few parameters. Expected 2. I've read that I may need to build out the SQL in VBA using the form parameters, but it would be pretty complex SQL given that there are a few queries in the chain.
Any suggestions as to a workaround? I considered using VBA to create a table from the query and then just referencing that table--I hate to make extra steps though.
The reason you get the error when you just try to open the recordset is that your form is not open and when you try to access [forms]![frmReportingMain] it's null then you try to get a property on that null reference and things blow up. The OpenRecordset function has no way of poping up a dialog box to prompt for user inputs like the UI does if it gets this error.
You can change your query to use parameters that are not bound to a form
yourTableAllocStart >= pAllocStart
and yourTableAllocEnd <= pAllocEnd
Then you can use this function to get the recordset of that query.
Function GetQryAllocDebits(pAllocStart As String, pAllocEnd As String) As DAO.Recordset
Dim db As DAO.Database
Dim qdef As DAO.QueryDef
Set db = CurrentDb
Set qdef = db.QueryDefs("qryAlloc_Debits")
qdef.Parameters.Refresh
qdef.Parameters("pAllocStart").Value = pAllocStart
qdef.Parameters("pAllocEnd").Value = pAllocEnd
Set GetQryAllocDebits = qdef.OpenRecordset
End Function
The disadvantage to this is that when you call this now on a form that is bound to it it doesn't dynamically 'fill in the blanks' for you.
In that case you can bind forms qryAlloc_debts and have no where clause on the saved query, then use the forms Filter to make your where clause. In that instance you can use your where clause exactly how you have it written.
Then if you want to still open a recordset you can do it like this
Function GetQryAllocDebits(pAllocStart As String, pAllocEnd As String) As DAO.Recordset
Dim qdef As DAO.QueryDef
Set qdef = New DAO.QueryDef
qdef.SQL = "Select * from qryAlloc_Debits where AllocStart >= pAllocStart and pAllocEnd <= pAllocEnd"
qdef.Parameters.Refresh
qdef.Parameters("pAllocStart").Value = pAllocStart
qdef.Parameters("pAllocEnd").Value = pAllocEnd
Set GetQryAllocDebits = qdef.OpenRecordset
End Function
While a [Forms]!... reference does default to a form reference when a QueryDef is run from the GUI, it is actually just another Parameter in the query in VBA. The upshot is you don't have to recode your query/create a new one at all. Also, as #Brad mentioned, whether a parameter is in the final query of a chain of queries or not, you are able to refer to the parameter as if it is in the collection of the final query. That being the case, you should be able to use code similar to this:
Sub GetQryAllocDebits(dteAllocStart As Date, dteAllocEnd as Date)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
Set db = CurrentDb()
Set qdf = db.QueryDefs("qryAlloc_Debit")
If CurrentProject.AllForms("frmReportingMain").IsLoaded Then
qdf.Parameters("[forms]![frmReportingMain]![txtAllocStart]") = [forms]![frmReportingMain]![txtAllocStart]
qdf.Parameters("[forms]![frmReportingMain]![txtAllocEnd]") = [forms]![frmReportingMain]![txtAllocEnd]
Else
qdf.Parameters("[forms]![frmReportingMain]![txtAllocStart]") = CStr(dteAllocStart)
qdf.Parameters("[forms]![frmReportingMain]![txtAllocEnd]") = CStr(dteAllocEnd)
End If
Set rst = qdf.OpenRecordset
Do Until rst.EOF
'...do stuff here.
Loop
Set rst = Nothing
Set qdf = Nothing
Set db = Nothing
End Function
If the referenced form is open, the code is smart enough to use the referenced controls on the form. If not, it will use the dates supplied to the subroutine as parameters. A gotcha here is that the parameters did not like when I set them as date types (#xx/xx/xx#), even if the field were dates. It only seemed to work properly if I set the params as strings. It didn't seem to be an issue when pulling the values straight out of the controls on the forms, though.
I know it's been a while since this was posted, but I'd like to throw in my tuppence worth as I'm always searching this problem:
A stored query can be resolved:
Set db = CurrentDb
Set qdf = db.QueryDefs(sQueryName)
For Each prm In qdf.Parameters
prm.Value = Eval(prm.Name)
Next prm
Set rst = qdf.OpenRecordset
For SQL:
Set db = CurrentDb
Set qdf = db.CreateQueryDef("", "SELECT * FROM MyTable " & _
"WHERE ID = " & Me.lstID & _
" AND dWeekCommencing = " & CDbl(Me.frm_SomeForm.Controls("txtWkCommencing")) & _
" AND DB_Status = 'Used'")
For Each prm In qdf.Parameters
prm.Value = Eval(prm.Name)
Next prm
Set rst = qdf.OpenRecordset
This assumes that all parameter values are accessible - i.e. forms are open and controls have values.
'I have two parameters in my recordset and I was getting the "Too few parameters. Expected 2" 'error when using an OpenRecordset in MS Access vba, and this is how I got around it and IT WORKS! see the below sub routine:
'Private Sub DisplayID_Click()
'1. I created variables for my two parameter fields xEventID and xExID as seen below:
Dim db As Database
Dim rst As Recordset
Dim xEventID As Integer
Dim xExId As Integer
'2. Sets the variables to the parameter fields as seen below:
Set db = CurrentDb
xEventID = Forms!frmExhibitorEntry!txtEventID
xExId = Forms!frmExhibitorEntry!subExhibitors!ExID
'3. Set the rst to OpenRecordSet and assign the Set the variables to the WHERE clause. Be sure to include all quotations, ampersand, and spaces exactly the way it is displayed. Otherwise the code will break!exactly as it is seen below:
Set rst = db.OpenRecordset("SELECT tblInfo_Exhibitor.EventID,tblInfo_Display.ExID, tblMstr_DisplayItems.Display " _
& "FROM tblInfo_Exhibitor INNER JOIN (tblMstr_DisplayItems INNER JOIN tblInfo_Display ON tblMstr_DisplayItems.DisplayID = tblInfo_Display.DisplayID) ON tblInfo_Exhibitor.ExID = tblInfo_Display.ExID " _
& "WHERE (((tblInfo_Exhibitor.EventID) =" & xEventID & " ) and ((tblInfo_Exhibitor.ExID) =" & xExId & " ));")
rst.Close
Set rst = Nothing
db.Close
'End Sub
I am trying to retrieve a value from a GridView and save it as a Date variable, using the following code:
Dim invoice_date As Date = Convert.ToDateTime((TryCast(row.Cells(2).Controls(0), TextBox)).Text)
I am getting an error:
System.Web.UI.WebControls.TextBox) returned Nothing.
From what I have read, if the TryCast fails, it will return a value of nothing.
I have a similar command for an Integer, which works fine on another column.
Dim order_value As Integer = Convert.ToInt32((TryCast(row.Cells(3).Controls(0), TextBox)).Text)
Can somebody please help with the correct syntax to work with a Date value?
You are right that TryCast returns Nothing if it cannot cast to the target type. So, the problem that you have is most likely that row.Cells(2).Controls(0) is not a TextBox. To debug this, split the line:
Dim invoice_date As Date = Convert.ToDateTime((TryCast(row.Cells(2).Controls(0), TextBox)).Text)
like this:
Dim textbox As TextBox = TryCast(row.Cells(2).Controls(0), TextBox)
Dim invoice_date As Date = Convert.ToDateTime(textbox.Text)
and look at the value of textbox.
Try and use DirectCast() and see if that works.
I know I'm being an idiot here and I just can't work it out. But i'm trying to take some data back from a vb.net database. It's falling over with a Object reference not set to an instance of an object error. And before the code runs it's saying the variable is being used before it's set, but I can't see how. Code:
Private taNotifications As dsDataTableAdapters.NotificationsTableAdapter = New dsDataTableAdapters.NotificationsTableAdapter
Dim notification As dsData.NotificationsDataTable = taNotifications.GetDataByClientID(userrow.UserID)
If notification.Rows.Count > 0 Then
Dim notificationrow As dsData.NotificationsRow
Dim forwardURL As String = notificationrow.ForwardLocation
End If
It falls over on the Dim forwardURL As String = notificationrow.ForwardLocation
The problem is that you have never instantiated the notificationRow inside the if statement. You've declared it, but it doesn't belong to anything. You need to make an assignment or loop through your rows before doing anything with this object:
Dim notificationrow As dsData.NotificationsRow ' this is not instantiated
Dim forwardURL As String = notificationrow.ForwardLocation
What you really want in this case is:
For Each notificationRow As dsData.NotificationRow In notification
Dim forwardURL As String = notificationRow.ForwardLocation
' Do Something
Next
If you only HAVE one row and you know you only have 1 or 0 rows then you could use your if statement by doing:
If notification.Rows.Count > 0 Then
Dim notificationrow As dsData.NotificationsRow = _
CType(notification.Rows(0), dsData.NotificationsRow)
Dim forwardURL As String = notificationrow.ForwardLocation
End If
Edit: In the code above, I originally just had notification.Rows(0). This will produce a DataRow object, but it will not be strongly typed. You need to perform the CType that I added in order to use the custom property ForwardLocation.
You never set notificationrow to anything. Did you mean to set it like this?
Dim notificationrow As dsData.NotificationsRow = CType(notification.Rows(0), dsData.NotificationsRow)
I have a form where two fields on the first page of the form make up the primary key. I want to check for duplicate values before attempting to insert the record, since I don't want the user to go all the way through the form only to find out they can't submit it. So I'm trying to check for duplicate values when the user tries to go to the next page of the form. I wasn't quite sure how to do it, and sure enough I'm getting an error. ("Object reference not set to an instance of an object.") The problem is apparently in my if statement, "If myValue.Length > 0 Then", but I'm not sure what needs to be in place of that.
Protected Sub CustomValidator1_ServerValidate(ByVal source As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs) Handles CustomValidator1.ServerValidate
'get values
Dim checkPrefix = txtCoursePrefix.Text
Dim checkNum = txtCourseNum.Text
'db connectivity
Dim myConn As New OleDbConnection
myConn.ConnectionString = AccessDataSource1.ConnectionString
myConn.Open()
'select records
Dim mySelect As New OleDbCommand("SELECT prefix, course_number FROM tableCourse WHERE prefix='checkPrefix' AND course_number='checkNum'", myConn)
'execute(Command)
Dim myValue As String = mySelect.ExecuteScalar()
'check if record exists
If myValue.Length > 0 Then
CustomValidator1.ErrorMessage = "some exp text"
CustomValidator1.SetFocusOnError = "true"
CustomValidator1.IsValid = "false"
End If
End Sub
Thought I'd post the final solution:
'select records
Dim mySelect As New OleDbCommand("SELECT 1 FROM tableCourse WHERE prefix=? AND course_number=?", myConn)
mySelect.Parameters.AddWithValue("#checkPrefix", checkPrefix)
mySelect.Parameters.AddWithValue("#checkNum", checkNum)
'execute(Command)
Dim myValue = mySelect.ExecuteScalar()
'check if record exists
If myValue IsNot Nothing Then
CustomValidator1.SetFocusOnError = True
args.IsValid = False
End If
This error indicates that the content of myValue variable is null. If it's null you can't use Length property (or any other property for that matter) on it. You have to check for null explicitly:
If myValue IsNot Nothing Then
EDIT 1
Your sql query is wrong. I don't know what would be the right query, as I don't know your database, but I think you intender to write this:
Dim mySelect As New OleDbCommand("SELECT prefix, course_number FROM tableCourse WHERE prefix=" + checfkPreix + " AND course_number=" + checkNum, myConn)
or something to that effect. You might want to consider using string.Format function for forming the string. And you also need to make sure that there is some kind of protection against SQL Injection, since you form your query from user input. In your case using of OleDbParameter might be appropriate.
Edit 2
You also right to mention that there might be a problem with ExecuteScalar. ExecuteScalar is supposed to return a single value and your select query are returning two (prefix and course_number). Change it so that it returns a single parameter SELECT prefix FROM or simply SELECT 1 FROM and then the rest of the query:
Dim mySelect As New OleDbCommand("SELECT 1 FROM tableCourse WHERE prefix=? AND course_number=?", myConn)
mySelect.Parameters.AddWithValue("#checkPrefix", checkPrefix)
mySelect.Parameters.AddWithValue("#checkNum", checkNum)
Edit 3
You are not setting failed validation properly in your validator.
Add
args.IsValid = False
inside your if statement.
First ExecuteScalar will only return a single value, so in this case you are only going to get the column prefix from the result. Second if there is no match with your query it will return null, so your next length check should account for that scenario:
if String.IsNullOrEmpty(myValue) Then
...
Reference: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalar.aspx
myValue is null if there is no duplicate, so you have to apply .Length only if myValue is not null (which means checking for null only is enough; without .Length)
If Not string.IsNullOrEmpty(myValue) Then
try something like this instead (you will have to adapt it to VB.Net) DBNull is different from Null or Nothing so you have to compare it to both
If myValue <> DBNull and not myvalue is nothing Then
I'm just stumped on what to do with this code, I'm just trying to implement a 'no duplicates' catch on my insert customer form, but it just slips through my if statement to the else everytime. This is the source. Also I tried a .Equals with the same results :(
Protected Sub srcAllClients_Inserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceCommandEventArgs) Handles srcAllClients.Inserting
'Establish Variables
Dim emailAddress As String
Dim srcUsers As SqlDataSource = New SqlDataSource()
srcUsers.ConnectionString = ConfigurationManager.ConnectionStrings("ISSD21ConnectionString").ConnectionString
Dim view As DataView
view = DirectCast(srcUsers.Select(DataSourceSelectArguments.Empty), DataView)
srcUsers.SelectCommand = "SELECT EmailAddress FROM ISSDClients"
srcUsers.DataSourceMode = SqlDataSourceMode.DataReader
Dim reader As IDataReader
reader = DirectCast(srcUsers.Select(DataSourceSelectArguments.Empty), IDataReader)
emailAddress = FormView1.FindControl("txtEmail").ToString
While reader.Read()
If reader("EmailAddress") = (emailAddress) Then
lblError.Text = "Your Email is NOT Unique!"
'this is where we cancel the update and return an error
Else
lblError.Text = "Your Email is Unique!"
'nothing needs to happen, maybe just tell them that it went through
End If
End While
reader.Close()
End Sub
emailAddress = FormView1.FindControl("txtEmail").ToString
is just going to return the string "System.Web.UI.WebControls.TextBox". You're not accessing the actual property of the control that would hold the text value, you're just calling ToString() on the control itself.
Try this:
Dim emailBox As TextBox = CType(FormView1.FindControl("txtEmail"), TextBox);
emailAddress = emailBox.Text
In addition to Womp's answer...
In the while loop that is running through the email records, you need to break out of the loop once you find a matching email and alert the user.
if reader("EmailAddress") = (emailAddress) then
'1. Break from the Loop
End if
I would recommend that you pass the emailAddress to the SQL Server as a parameter.
Select Count(EmailAddress) From ISSDClients
Where EmailAddress = #EmailAddress
Execute this statement using ExecuteScalar and cast the result as an integer. If the result is zero then you are ok, otherwise display an error.
Doing it this way avoids using the while loop and should be much quicker if your table has lots of rows.
You also need to get the Text property from the Email Text box.
emailAddress = FormView1.FindControl("txtEmail").Text.ToString
You may want to take a look at the String.Compare method, which will make it easier to compare without respect to things like case sensitivity and culture. It does consider whitespace to be part of your string, so you may wish to trim the string prior to calling it, to help normalize.
For example, the following strings would be considered equal:
var firstString = "some StrinG to Compare ";
var secondString = " somE string to COMPARE";
var equal = (String.Compare(firstString.Trim(), secondString.Trim(), StringComparison.InvariantCultureIgnoreCase) == 0);