Converting Empty String to NULL Double - sql

I have spent much of the day trying to find an answer for this one. I figured it would be easy but nothing is specific to what I am trying to do. So I hope someone can help.
I am bringing values over from one system to another. I have a few fields that come over as strings but they need to go into the new system as DOUBLE. The problem occurs when there are empty strings ("") and it is trying to store this as double. I have tried EVERYTHING.
dbnull.value
Double.TryParse
CDBL()
Double?
VAL()
etc...cannot get this to work and I am not sure why.
so here is the code:
Dim specvertclr As String = dt23.Rows(0).ItemArray.GetValue(38).ToString()
then when I insert this into my database I get the error because the field type is NUMERIC and I am trying to insert ""
I did have this working:
Dim specvertclr As String
If dt23.Rows(0).ItemArray.GetValue(38).ToString() = "" Then
specvertclr = CStr(0)
Else
specvertclr = dt23.Rows(0).ItemArray.GetValue(38).ToString()
End If
But the problem with this is it inserts a 0 value and 0 is not the same as NULL. I want the NUMERIC field in my destination database (SQLCE) to be EMPTY when the string is empty from the source database.
Any help?
This is what I tried:
Dim specvertclr As Nullable(Of Double) = CType(dt23.Rows(0).ItemArray.GetValue(38).ToString(), Double?)
and then my insert is basic and the program throws an error before it even gets here. I know this part is correct.
Dim cmd2 As SqlCeCommand = conn.CreateCommand()
cmd2.CommandText = "Insert into [Attr_Bridge] ([VERTCLR]) VALUES (?)"
With cmd2.Parameters
.AddWithValue("P1", specvertclr)
end with

You need to check the content of your string before adding it to the database
Dim specvertclr = dt23.Rows(0).ItemArray.GetValue(38).ToString()
....
Dim cmd2 As SqlCeCommand = conn.CreateCommand()
cmd2.CommandText = "Insert into [Attr_Bridge] ([VERTCLR]) VALUES (#P1)"
With cmd2.Parameters
.Add("#P1", SqlDbType.Float).Value = If(String.IsNullOrEmpty(specvertclr), DBNull.Value, CType(specvertclr, Object))

This turned out to be the answer .... Thank You Arminius for your linked article that lead me to right answer ... Thank you Steve for your time and help!
Dim specvertclr As String = dt23.Rows(0).ItemArray.GetValue(38).ToString()
Dim oerrorspec As Object = System.DBNull.Value
If specvertclr <> Nothing Then oerrorspec = specvertclr
and then in the parameters section I used this
.Add(New SqlCeParameter("#P41", oerrorspec))

I came across this question because I had the same issue. I was creating an app and one of the inputs was optional and I didn't want users to worry about putting zero so they don't receive an error. So my question was how to convert "" string to double? I came up with the below:
Dim Nontaxable As Double
If txtNontaxable.Text IsNot "" Then
Nontaxable = CDbl(txtNontaxable.Text)
Else
txtNontaxable.Text = 0
End If
if the string isn't blank, it will be converted fine, if its blank, it will be replaced with zero, and then it will be converted fine. Hope this helps anyone had the same issue.

Related

vb.net Interpolated Strings

I was chastised by a professional developer with a lot of years of experience for Hard Coding my DB name
OK I get it we sometimes carry our bad codding habits with us till we learn the correct way to code
I have finally learned to use Interpolated Strings (personal view they are not pretty)
My Question involves the two Sub's posted below GetDB runs first then HowMany is called from GetDB
Sorry for stating the obvious my reason is I think that NewWord.db gets declared in GetDB and works in HowMany without the same construction Just a Wild Guess
Notice NO $ or quotation used in HowMany
Both Sub's produce desired results
The question is Why don't both statements need to be constructed the same?
Public Sub HowMany()
'Dim dbName As String = "NewWord.db"
Dim conn As New SQLiteConnection("Data Source ='{NewWord.db}';Version=3;")
tot = dgvOne.RowCount ' - 1
tbMessage.Text = "DGV has " & tot.ToString & " Rows"
End Sub
Private Sub GetDB()
Dim str2 As String
Dim s1 As Integer
'Dim dbName As String = "NewWord.db"
Using conn As New SQLiteConnection($"Data Source = '{"NewWord.db"}' ;Version=3;")
conn.Open()
That second method is a ridiculous and pointless use of string interpolation. What could possibly be the point of inserting a literal String into a literal String? The whole point is that you can insert values determined at run time. That second code is equivalent to using:
"Data Source = '" & "NewWord.db" & "' ;Version=3;"
What's the point of that? The idea is that you retrieve your database name from somewhere at run time, e.g. your config file, and then insert that into the template String, e.g.
Dim dbName = GetDbNameFromExternalFile()
Using conn As New SQLiteConnection($"Data Source = '{dbName}' ;Version=3;")
Now the user can edit that external file to change the database name after deploying the application. How could they change the name in your code?
To be clear, string interpolation is just native language support for the String.Format method. You can see that if you make a mistake that generates an exception and the that exception will refer to the String.Format method. In turn, String.Format is a way to make code that multiple values into a long template easier to read than if multiple concatenation operators were used.
Having lots of quotes and ampersands makes code hard to read and error-prone. I've lost count of the number of times people miss a single quote or a space or the like in a String because they couldn't read there messy code. Personally, I'll rarely use two concatenation operators in the same expression and never three. I'll do this:
Dim str = "some text" & someVar
but I'll rarely do this:
Dim str = "some text" & someVar & "some more text"
and I'll never do this:
Dim str = "some text" & someVar & "some more text" & someOtherVar
Before string interpolation, I would use String.Format:
Dim str = String.Format("some text{0}some more text{1}", someVar, someOtherVar)
Nowadays, I'll generally use string interpolation:
Dim str = $"some text{someVar}some more text{someOtherVar}"
Where I may still use String.Format over string interpolation is if one value is getting inserted in multiple places and/or where the text template and/or the expressions are long so that I can break the whole thing over multiple lines, e.g.
Dim str = String.Format("some text{0}some more text{1}yet more text{0}",
someVar,
someOtherVar)
I have no idea what NewWord.db is so I made a class to represent it.
Public Class NewWord
Public Shared Property db As String = "The db Name"
End Class
HowMany is not a very good name for your sub. Try to use more descriptive names.
The first sub doesn't even use the connection. The connection string in that code is a literal string. It will not consider NewWord.db as a variable. You will not notice this because you never attempt to open the connection. In my version you check the connection string with a Debug.Print.
I changed the last line to use and interpolated string. It is not necessary to call .ToString on tot.
Private Sub DisplayGridCount()
Dim conn As New SQLiteConnection("Data Source ='{NewWord.db}';Version=3;")
Debug.Print(conn.ConnectionString)
Dim tot = DataGridView1.RowCount
TextBox1.Text = $"DGV has {tot} Rows"
End Sub
The second snippet starts off with 2 unused variables. I deleted them. Again, the Debug.Print to show the difference in the 2 strings.
Private Sub TestConnection()
Using conn As New SQLiteConnection($"Data Source = '{NewWord.db}' ;Version=3;")
Debug.Print(conn.ConnectionString)
'conn.Open()
End Using
End Sub
As to where to store connection strings see https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/protecting-connection-information and Where to store Connection String

OverFlowException on Executescalar on vb.net

I'm working in asp.net with vb.net and in the backend I'm trying to select something from the database.
But whenever I ask to execute the query it gives an error which says 'OverflowException Ocuured'. The query that is made works perfectly in my SQL Manager tool. Any ideas what can be the problem.
(it gives the problem on the line under 'try' so the 'returnedId = com.ExecuteScalar' line)
Function selectEIDCardnumber(ByVal name As String) As Integer
Dim con As New Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("testdatabase").ConnectionString)
Dim selecter As String = "SELECT EIDCardNumber FROM [dbo].[_User] where DisplayName = #name"
Dim com As New SqlCommand(selecter, con)
com.Parameters.AddWithValue("#name", name)
con.Open()
Dim returnedId As Integer = 0
Try
returnedId = com.ExecuteScalar
Catch ex As Exception
Response.Redirect("oops.aspx")
End Try
con.Close()
Return returnedId
End Function
The result of com.ExecuteScalar is larger than the max int value.
Int32.TryParse(com.ExecuteScalar, returnedId)
By defining your returnedId and Function As type Integer you are telling VB that the value will be a whole number between -2,147,483,648 and 2,147,483,647. Assuming the data in your database is correct, the solution is to change the returnedId and Function types to Int64, which holds numbers up to 9,223,372,036,854,775,807.
If you're sure that the EIDCardNumber column is an integer type column then try changing Dim returnedId As Integer = 0 into Dim returnedId As Long = 0 and if the problem persists try changing the value of selecter to "SELECT top 1 EIDCardNumber FROM [dbo].[_User] where DisplayName = #name"

Is it possible to change the default decimal separator in float.ToString()?

I need to send data in string format to mysql. By default vb.net interprets 0.5 as 0,5 which MySql won't accept. I know I could write floatval.tostring.replace(",", ".") to make it fit but I was wondering if it was possible to make it more comfortable so that an implicit conversion from float to string would produce a dot instead of a comma?
EDIT: per request, current code
Public Sub InsertInto(Values As IEnumerable(Of String))
Dim ValStr As String = ""
For Each V In Values
ValStr &= "'" & V & "',"
Next
Dim Command = New MySqlCommand("INSERT INTO " & Table & " VALUES (" & ValStr.Substring(0, ValStr.Length - 1) & ");", Connection)
Command.ExecuteNonQuery()
End Sub
this method is a part of a mysql connection wrapper and the properties "Connection" and "Table" are preassigned.
My test code calls the function as follows:
dimdum.InsertInto({"DEFAULT", (0.5).ToString.Replace(",", "."), "here is text"})
the test table columns are auto iterating int as primary key, a float and a varchar
As I have saind in my comment above, I am afraid that you need to revise a lot of your code. As is you have a lot of problems, the worst is the Sql Injection that sooner or later you have to fix, but your try to convert everything in a string has also the drawback that the conversion of decimals, dates and other floating points values give more immediate troubles than the Sql Injection one.
There is only one way to get out and it is the use of parameterized queries. More code to write but after a while it is very straightforward.
So for example you should rewrite your code to something like this
Public Sub InsertInto(sqlText As String, Values As List(Of MySqlParameter))
Using Connection = New MySqlConnection(... connectionstring here (or a global variable ....)
Using Command = New MySqlCommand(sqlText, Connection)
Connection.Open()
If Values IsNot Nothing Then
Command.Parameters.AddRange(values.ToArray)
End If
Command.ExecuteNonQuery()
End Using
End Using
End Sub
and call it with this
Dim decValue As Decimal = 0.5
Dim strValue As String = "Test"
Dim dateValue As DateTime = DateTime.Today
Dim parameters = New List(Of MySqlParameter)()
parameters.Add(New MySqlParameter() With { .ParameterName = "#p1",
.DbType = MySqlDbType.Decimal,
.Value = decValue})
parameters.Add(New MySqlParameter() With {.ParameterName = "#p2",
.DbType = MySqlDbType.String,
.Value = strValue})
parameters.Add(New MySqlParameter() With {.ParameterName = "#p3",
.DbType = MySqlDbType.Date,
.Value = dateValue})
InsertInto("INSERT INTO youTable VALUES(#p1, #p2, #p3)", parameters)
Note that now InserInto is just a simple routine that receives the command text and the parameters expected by the text, add them to the command, opens the connection, executes everything and exits closing the connection.
Note also that, with a parameterized queries, your sql command is totally void of the mess caused by single quotes for strings, formatting rules for dates and the handling of the decimal point is nowhere in sight
(A side note. This INSERT INTO text suppose that your table has exactly three fields and you supply the values for all of them, if you want to insert only a subset of fields then you need to pass them to the method as a third parameter )
Specify CultureInfo:
Dim n As Single
Dim s As String
n = Math.PI
s = n.ToString("F2", New System.Globalization.CultureInfo("en-US"))
s will be "3.14", even if your computer is set for a different format.

avoid checking for DataRow.IsDBNull on each column?

My code is 2x longer than it would be if I could automatically set IsDBNull to "" or simply roll over it without an error.
This is my code:
Dim conn As New SqlConnection
conn.ConnectionString = Module1.DBConn2
Dim sqlCommand = New SqlCommand("SELECT * FROM table", conn)
conn.Open()
Dim sqlDataset As DataSet = New DataSet()
Dim sqlDataAdapter As SqlDataAdapter = New SqlDataAdapter(sqlCommand)
sqlDataAdapter.Fill(sqlDataset)
conn.Close()
For Each rs As DataRow In sqlDataset.Tables(0).Rows
If Not IsDBNull(rs("column")) Then
Response.Write(rs("column"))
Else
Response.Write("")
End If
Response.Write("some stuff to write")
If Not IsDBNull(rs("column2")) Then
Response.Write(rs("column2"))
Else
Response.Write("")
End If
Next
In that case I'd just like to type Response.Write(rs("column")) instead of the If statement, and if column IsDBNull then output an empty string.
How can I do this?
Many thanks in advance!
You could simply use String.Join and pass row.ItemArray:
For Each row As DataRow In sqlDataset.Tables(0).Rows
Response.Write(String.Join("", row.ItemArray))
Next
That works since DBNull.ToString returns an empty string.
If you want to address every column, you can use the strongly typed DataRowExtensions.Field method which supports nullables and return null/Nothing for string. Then you could use the null-coalescing operator (?? in C#, If in VB).
Dim rowInfo = String.Format("{0}{1}{2}",
If(row.Field(Of String)("Column1"), ""),
If(row.Field(Of String)("Column2"), ""),
If(row.Field(Of String)("Column3"), ""))
However, note that String.Format will convert null/Nothing to "" implicitely anyway, so the If is redundant and just fyi.
MSDN:
If the object specified by index is a null reference (Nothing in
Visual Basic), then the format item is replaced by the empty string
("").
Here's a one-liner:
Response.Write(rs.IsNull("column") ? "" : rs("column"));
or make it an extension method:
public string GetValueOrBlankString(this DataRow rs, string column)
{
return rs.IsNull(column) ? "" : rs(column).ToString();
}
then call it as:
Response.Write(rs.GetValueOrBlankString("column"));
Dataset Extensions give you a clean way of doing and it's also strongly typed. The type must match the column type in the database though. If the database column can be null, then use a nullable type like below. The null values become Nothing for the returned nullable type.
For Each rs As DataRow In sqlDataset.Tables(0).Rows
'If string, you can use this. Null becomes nothing for the string.
Response.Write(rs.field(of String)("column"))
'if it's another type
Response.Write(rs.field(of Integer?)("column"))
Next
Ceres's answer is probably the best given that it avoids any sort of null testing, but it's worth noting that the 'IIF' function would also work pretty well her. It's still going to do the test for null but it's much more compact than how Joe was originally doing it. Something like this should do the trick:
For Each rs As DataRow In sqlDataset.Tables(0).Rows
Response.Write( IIF( IsDBNull(rs("column")), "", rs("column") ) )
Next
What's neat with this is you can substitute the "" for whatever you want to output if the value is in fact null ( a nice little added bonus. )
Here's some info on the 'IIF' function for those who don't know what it is:
http://msdn.microsoft.com/en-ca/library/27ydhh0d(v=vs.71).aspx

VB.Net Convert ToDecimal won´t work with comma

I´m trying to parse a String to a Decimal but I keep getting a formatException
Dim row as GridViewRow
for each row in grdActieRittenActiefAlt.rows
Dim rbl as RadioButtonList = row.FindControl("tblAddAct")
'toevoegen = true
if rbl.SelectedItem.Value = true then
Dim opgaveIdent as Integer = Convert.ToInt32(grdActieRittenActiefAlt.DataKeys(row.RowIndex).Value.ToString())
Dim curOldTarief as Decimal = Convert.ToDecimal(lblCuratiefTarief.Text)
Dim curOldKorting as Decimal = Convert.ToDecimal(lblCuratiefKorting.Text)
Dim curTariefString as String = row.Cells(4).Text
Dim curKortingString as String = row.Cells(6).Text
Dim curTarief as Decimal = Convert.ToDecimal(curTariefString)
Dim curKorting as Decimal = Convert.ToDecimal(curKortingString)
lblCuratiefTarief.Text = (curOldTarief + curTarief).ToString()
lblCuratiefKorting.Text = (curOldKorting + curKorting).ToString()
end if
next row
The input is 431,25.
So far I've tried the following:
Changing the comma to a colon using .Replace(",",".") => Didn't work
Using a forced CultureInfo => Didn't work
Use the row.Cells(4).Text directly => Didn't work
Use a substring query to get only the round numbers (431) => Worked but is no solution
Does anyone else have any suggestions?
After doing a lot more testing and trying I've found a workaround/solution for the problem.
The row.Cells(4).Text was a boundfield. I've changed that to a TemplateField with a label.
By making a local variable (Label) and using that to convert the decimal from it works. I got no idea why but for me it's a solution for now.
Have you tried the FormatNumber function?
FormatNumber(number, 2)
2 is the number of decimal places.