Use IF() Null Coalesce in SQL Parameters - vb.net

I am trying to use the If() Operator to coalesce values in a SQL Parameter but I am having trouble figured it out.
Dim First as String = Nothing
First = FirstName.Text
Using conn As New SqlConnection()
'Code omitted
.AddWithValue("#FirstName", If(First, DBNull.Value))
Basically, if First = Nothing, the Parameter should be formatted like:
.AddWithValue("#FirstName", DBNull.Value)
If FirstName.Text is not Nothing or Null then it would basically be like:
.AddWithValue("#FirstName", First)
Anyone have any pointers or have any suggestions?
I understand that First is carrying a value of "", I am just not sure how to program around that. That's why I tried = Nothing.
This is currently being written like:
If employeeName <> "last,first" Then
cmd.Parameters.AddWithValue("#EMPL_NM", employeeName)
Else
cmd.Parameters.AddWithValue("#EMPL_NM", DBNull.Value)
End If
Which I'm trying to reduce to
.AddWithValue("#FirstName", If(First, DBNull.Value))

DBNull.Value and String are different types. The null-coalescing operator doesn't like that.
You could cast it to Object to make it compile, although i would prefer your If-Else:
cmd.Parameters.AddWithValue("#FirstName", If(First, DirectCast(DBNull.Value, Object)))

Related

Input string was not in correct format | decimal/integer to "NULL"

I've recently started learning .net and I wrote a basic sql connected data entry form. Almost all of my data types are integer or decimals. Sometimes I need to remain empty the textboxes and enter "NULL" data and I receive with this error "Input string was not in correct format".
How I can fix this error ? Key point; I don't want to enter "0" it must be "NULL" in sql server because I use some charts to track all of these data, so when there is data equal the "0" its ruined my charts. As far as I understand from my researches Tryparse is fit for it but I couldn't find any information to use it properly in .net.
I'll share my transformation code below.
Try
Dim command As New SqlCommand()
command.Parameters.Add("A", SqlDbType.Decimal).Value = Decimal.Parse(A)
command.Parameters.Add("B", SqlDbType.Int).Value = Integer.Parse(B)
command.Parameters.Add("C", SqlDbType.Decimal).Value = Decimal.Parse(C)
command.Parameters.Add("D", SqlDbType.Decimal).Value = Decimal.Parse(D)
command.Parameters.Add("E", SqlDbType.Decimal).Value = Decimal.Parse(E)
command.Parameters.Add("F", SqlDbType.Decimal).Value = Decimal.Parse(F)
command.Parameters.Add("G", SqlDbType.Int).Value = Integer.Parse(G)
command.Parameters.Add("H", SqlDbType.Int).Value = Integer.Parse(H)
c.CUD(command, sql)
list()
clear()
MessageBox.Show("Data Successfully Saved")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
command.Parameters.Add("strokeTB", SqlDbType.Decimal).Value = If(strokeTB.TextLength = 0, CObj(DBNull.Value), Decimal.Parse(strokeTB.Text))
This assumes that you have already validated the contents of the TextBox and it is guaranteed to either be empty or contain a valid Decimal value.
This If operator is similar to the old IIf function but it's better because it short-circuits, i.e. the third argument operand is only evaluated if the first operand is False. Because IIf is a function, it evaluates all three arguments first, so Decimal.Parse would still be called and it would fail. Using If, Decimal.Parse will only be called if there is text to parse.

Converting Empty String to NULL Double

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.

Option Strict On and DBNull.Value

I've started converting an application of mine to use Option Strict On. I've been doing the CStr,Ctype etc, and it's been going well.
SQLCommand.Parameters.AddWithValue("#TERMINATE", If(IsNothing(txtEarningsTerminated.DateValue), DBNull.Value, txtEarningsTerminated.DateValue))
Then I hit this. The txtEarningsTerminated.DateValue is a custom masked text box. When it's value is Nothing I don't want to store anything in the database. However, it states
Cannot infer a common type, and Option Strict On does not allow 'Object' to be assumed.
When I change DBNull.Value to "" or nothing, the error goes away. However as Nothing, it fails during runtime stating
The parameterized query '(#CONTROL int,#CLIENTCODE nvarchar(10),#NoBill int,#TERMINATE nv' expects the parameter '#TERMINATE', which was not supplied.
I want to put a NULL in the database. This value can be a date and then become a NULL.
How do I translate this so as to not produce an error under Option Strict On?
The reason is because operator If(condition, executeAndReturnIfTrue, executeAndReturnIfFalse) expects that both true and false expressions will return same type.
In your case you return DbNull type if true and String(or some other type you didn't mentioned) if result is false.
If create SqlParameter more explicitly, then you can use next approach:
Dim value As Object = Nothing
If txtEarningsTerminated.DateValue Is Nothing Then
value = DbNull.Value
Else
value = xtEarningsTerminated.DateValue
End If
Dim param As SqlParameter = New SqlParameter With
{
.ParameterName = "#TERMINATE",
.SqlDbType = SqlDbType.VarChar, 'Or use your correct type
.Value = value
}
As mentioned in the comments using AddWithValue will force ADO.NET to decide the sql type automatically for your value, which can lead in different problems. For example everytime you run same query with different values your query plan will be recompiled every time you change value.
You can create extension method for string
Public Module ExtensionsModule
Public Function DbNullIfNothing(this As String) As Object
If this Is Nothing Then Return DBNull.Value
Return this
End Function
End Module
Then use it instead of your "inline If method"
Dim value As String = Nothing
Dim param As SqlParameter = New SqlParameter With
{
.ParameterName = "#TERMINATE",
.SqlDbType = SqlDbType.VarChar,
.Value = value.DbNullIfNothing()
}
So the answer was obvious and in the error. I just had to encase the DBNull.Value in a Ctype Object
CType(DBNull.Value, Object)
And then the code would compile just fine.
Here's one way to do it (assuming you're using stored procedures in your database):
In the stored procedure you're calling, set the date input parameter to equal null, making it an optional field with a default value of null (#terminate DATETIME = NULL).
That way, if you don't include that parameter in your procedure call, the procedure won't fail.
Set the default value of the date field to DateTime.MinValue so it always has some value. Then you can test to see if it is equal to a date other than DateTime.MinValue. If it is a valid date value, add the line to include the date parameter to the procedure call. If it is equal to DateTime.MinValue, don't add the parameter to the call.

Why doesn't my VB.Net code see the value that my SPROC is returning?

I've written some logic into a sproc that returns either 1 or 0 for true/false. Works great if I run it manually, but if I call it from VB I always get 0/false, no matter what the actual value the sproc returns. Probably missing something silly, but I'm just not seeing it. The code is partially copied from other code in our system that does work properly, but uses a dynamically constructed sproc call. I'm attempting to modify that code to use parameterized sproc calls.
The sproc returns the value in the column named "Allowed".
The VB code (copied and sanitized for variable names/content) looks like:
Public Function sample(ByVal parm1 As String, ByVal parm2 As String) As Boolean
Dim Allowed As Boolean = False
Try
Dim MyConnection As New SqlConnection(ConnString)
Dim MyDataAdapter As New SqlDataAdapter("sproc", MyConnection)
Dim DS As New DataSet
MyDataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure
MyDataAdapter.SelectCommand.Parameters.Add(New SqlParameter("#parm1", SqlDbType.Char, 14))
MyDataAdapter.SelectCommand.Parameters("#parm1").Value = parm1
MyDataAdapter.SelectCommand.Parameters.Add(New SqlParameter("#parm2", SqlDbType.Char, 30))
MyDataAdapter.SelectCommand.Parameters("#parm2").Value = parm2.ToUpper()
MyDataAdapter.Fill(DS)
If DS.Tables.Count > 0 Then
If DS.Tables(0).Rows.Count > 0 Then
Allowed = Convert.ToBoolean(DS.Tables(0).Rows(0).Item("Allowed"))
End If
End If
Catch ex As Exception
' Real code has error handling here that's not getting hit
End Try
Return Allowed
End Function
if you are only returning a true/false you should use execute scalar. there is a good example here
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalar.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-3
I would make sure to cast your value in the sproc as a bit something like:
select cast(1 as bit)
Select 1 and select Cast(1 as bit) will be two different things.

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