Assign null value to data row - vb.net

I am trying to write one line IF condition when assign value to property.
I've tried these syntax in VB.NET, type_of_documents is nullable integer:
1) vehicle.type_of_documents = If(vehicle.Istype_of_documentsNull, SqlTypes.SqlInt32.Null, vehicle.type_of_documents)
2) vehicle.type_of_documents = If(vehicle.Istype_of_documentsNull, DBNull.Value, vehicle.type_of_documents)
3) vehicle.type_of_documents = If(vehicle.Istype_of_documentsNull, Nothing, vehicle.type_of_documents)
Well, I am a little pushy to do this in one line. Somehow, all these syntax have failed to assign null value to my database. Syntax 1 & 2 have thrown cast integer exception. Syntax 3 is no error but no change/update value in database (same as previous).
Can anyone show me better syntax? Since I am really not into VB.NET.
Thanks in advance

The If operator is basically generic, i.e. the type it returns is inferred from the type of the second and third arguments. That means that those two arguments have to be the same type or one must assignable from the type of the other. A simple cast is all you need:
vehicle.type_of_documents = If(vehicle.Istype_of_documentsNull,
CObj(DBNull.Value),
vehicle.type_of_documents)
Now that the first argument is specified to be type Object, the two arguments share a type.

Return type of If method will be inferred from result parameters (as #jmcilhinney already answered).
In 1 and 2 samples you get cast exception because Integer cannot be casted to SqlTypes.SqlInt32.Null and DbNull.Value types.
Third sample compile because Nothing can be introduced as Integer, it simply default value of Integer which equals 0.
If you want return DbNull.Value in case when type_of_documents has no value - you need cast results in their common type, which is Object.
Instead of If method you can create extension method which return common type(object) required by SqlParameters
Public Module Extensions
<Extension>
Public Function GetValueOrDbNull(this As Integer?) As Object
If this.HasValue Then Return this
Return DBNull.Value
End Function
End Module
And use it
Dim sqlValue = vehicle.type_of_documents.GetValueOrDbNull()

Related

Error Operator '<>' is not defined for types 'System.Diagnostics.Process' and 'System.Diagnostics.Process'

Error: Operator <> is not defined for types System.Diagnostics.Process and System.Diagnostics.Process.
Function With error
Public Shared Function PornesteJocul() As Boolean
Return Process.GetProcessesByName(DatePatch.BinaryName).FirstOrDefault(Function(p) p.MainModule.FileName.StartsWith("")) <> CType(Nothing, Process)
End Function
Thanks.
If you want to compare something to Nothing then just compare it. There's no need to cast Nothing as a specific type. Also, you use = and <> only for value equality. That means for value types (structures) and for some very few reference types where value equality makes sense, e.g. for Strings where you care that two String objects contain the same characters and not whether two references refer to one String object. For reference equality you use Is and IsNot:
Return Process.GetProcessesByName(DatePatch.BinaryName).FirstOrDefault(Function(p) p.MainModule.FileName.StartsWith("")) IsNot Nothing
If what you actually care about is whether or not there is a match though, rather than what that match is, you don't need a comparison:
Return Process.GetProcessesByName(DatePatch.BinaryName).Any(Function(p) p.MainModule.FileName.StartsWith(""))

Why are missing `Set` for certain types a runtime error rather than compile error?

Given the following VBA code, assuming that a Something is just a VBA class module....
Public Type Foo
SomeThing As Something
End Type
Public Sub TestFoo()
Dim x As Foo
With x
'Correct way to do it
Set .someThing = New Something
End With
With x
'This is wrong but realized only as a RTE
'438: Object doesn't support this property or method
.SomeThing = New Something
End With
End Sub
In contrast, if you change the type to something like VBA.Collection as below:
Public Type Foo
SomeThing As VBA.Collection
End Type
Public Sub TestFoo()
Dim x As Foo
With x
.SomeThing = New VBA.Collection
End With
End Sub
This is now a compile error, with Argument Not Optional. This is obviously wrong but why is it a compile-time error only with VBA.Collection?
This is explained in the VBA Language Specification. The semantics of the assignment inside the With block is driven by whether or not the statement is a Set statement or a Let statement. In this context, it is a Let statement:
With x
.SomeThing = New Something
End With
Note that in the VBA grammar, the keyword Let is optional (obsolete):
let-statement = ["Let"] l-expression "=" expression
In a Set statement, the Set keyword is required:
set-statement = "Set" l-expression "=" expression
Inside of a With block, the l-expression is basically the UDT member, although the exact same behavior applies if x is used directly.
When evaluating a Let expression, the semantics are described in section 5.4.3.8:
Static Semantics.
This statement is invalid if any of the following is true:
<expression> cannot be evaluated to a simple data value (section 5.6.2.2 ).
Following that to 5.6.2.2 (Evaluation to a simple data value), the following runtime semantics apply (applicable rule only):
Runtime semantics.
At runtime, the simple data value’s value and value type are
determined based on the classification of the expression, as follows:
If the expression’s value type is a specific class:
If the source object has a public default Property Get or a public
default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Otherwise, if the source object does not have a public default
Property Get or a public default function, runtime error 438 (Object
doesn’t support this property or method) is raised.
Thus the runtime error 438 for SomeThing As Something.
With the Collection, the Let static semantics still apply, but it fails the static semantics of 5.6.2.2 (which gives the compile error). Again, the preceding inapplicable semantics are omitted:
Static semantics. The following types of expressions can be evaluated to produce a simple data value:
An expression classified as a value expression may be evaluated as a simple data value based on the following rules:
If the declared type of the expression is a specific class:
If this class has a public default Property Get or function and this default member’s parameter list is compatible with an argument
list containing 0 parameters, simple data value evaluation restarts as
if this default member was the expression.
The default member of Collection is a function (.Item) that takes a single parameter Index. In this code, the parameter is not provided, so the argument list is incompatible:
With x
.SomeThing = New VBA.Collection
End With
Thus the Argument Not Optional compile error.
In typing up the question, it struck me that the VBA.Collection had a default member. Thus, the compiler was interpreting the .Something = New VBA.Collection as an assignment to the default member... except that the Item is an indexed property. That explains why we get Argument not optional which would be quite a strange error to get with a Set statement.
In contrast, a VBA class module might not have a default member at all, so that there is no indexed property + default member to trigger a compile-time error. However, it also means that the bad syntax won't be caught until runtime.

Entity Framework: nullable DateTime toString(format) in vb.net handling

Given a table Element with a DateTime field birthday (not null), in Entity framework 4 I am allowed to do:
Dim mylist = (From el in Element Select el).ToList()
.Select(function(el) new with {
.bday = el.birthday.toString("dd/MM/yy")
}
I am now required to change birthday to be nullable, therefore I change the table in SQL server and then update my ModelDB in Visual studio.
The result is that the line:
.bday = el.birthday.toString("dd/MM/yy")
raises an exception as "Invalid cast from integer to string "dd/mm/yy").
The only way to get it "fixed" is to change the line this way:
.bday = CDate(el.birthday).toString("dd/MM/yy")
Is this the correct approach?
Now that I have a nullable Datetime, how to handle the case when el.birthday is null?
Thanks!
If you do a small experiment, you would understand perfectly what is happening:
Dim normalDate As Date = Now
Dim nullableDate As Nullable(Of Date) = normalDate
Dim normalToText As String = normalDate.ToString("dd/MM/yy") 'Works perfectly
Dim nullableToText As String = nullableDate.ToString("dd/MM/yy") 'Error
The content in both variables, normalDate and nullableDate, is identical but they are not: the ToString("date in certain format") functionality expects a Date type as input; what you are sending is a modified version of the Date type (not too different, but not the same either). With CDate what you are doing is converting the modified version of Date into an actually valid Date type and thus the ToString() functionality works without any problem.
Are you doing the right thing? Yes, as far as CDate can deal with "nulls" (CDate(Nothing) does not output any error): you are adapting the given variable to what ToString() expects.
NOTE: I have checked the exact error output by the code above and it effectively delivers a "Conversion from string "dd/MM/yy" to type 'Integer' is not valid.". Thus the error you are getting is the standard error when intending to use ToString("date") with a Nullable Date; not too descriptive error, this is true.
When dealing with nullable properties, the db column is mapped to System.Nullable(Of T) (See Documentation). So in your case, the el.birthday should have been mapped to Nullable(Of DateTime). This class has two properties to deal with the situation you have, that are, .Value (Doc) and .HasValue (Doc).
The actual type specific value is stored in .Value so in your case, you need to do something like this:
.bday = el.birthday.value.toString("dd/MM/yy")
Or you can use .HasValue which returns boolean to indicate if there is a value:
If el.birthday.HasValue Then
.bday = el.birthday.toString("dd/MM/yy")
End If

Setting a variable to null value in inline if statement

I'm trying to use the following code to check for a DBNull and set the variable to nothing if it is, or a short if it isn't. The problem is it is failing to set the variable to Nothing and sets it to a 0 instead. Anybody know why?
variable = If(currentRow.Item("variable") Is DBNull.Value,
Nothing, CShort(currentRow.Item("variable")))
If variable is declared As Short? then the code works with a slight tweak: you need to cast either operand of If to the target type first:
variable = If(condition, CType(Nothing, Short?), CShort(…))
(You could also have cast the third operand instead, or both.)
This cast is necessary because of how If deduces types: if the two result types mismatch, a common type is deduced which is the closest parent type, i.e. a type from which both inherit. However, with Nothing, new rules come into play because as far as VB is concerned, Nothing is already a valid Short – a default-initialised one (see old answer below for explanation). So VB doesn’t try any type coercion, it simply uses Short as the return value.
Old answer below, assuming that OP had declared variable As Short:
You cannot set value types to Nothing. If you assign Nothing to a value type then it will be set to its type’s default value instead – which is 0 for Short.
You can test this easily:
Dim s as Short = Nothing
Console.WriteLine(s)
Setting a value type to Nothing is the same as invoking its default constructor (New Short()) or declaring a new variable of that type without initialising it. The corresponding operation in C# would be to assign default(T) (short s = default(short)).
If you want to represent null value types, you have to use nullable types:
Dim s as Short? = Nothing
Now s is of type Nullable<Short> (Short? is a shortcut of that) and can be assigned a proper Nothing.

VB.NET Get underlying system.type from nullable type

I'm attempting to create a dataset based on the properties of an object. For example, I have an instance of a Person class with properties including ID, Forename, Surname, DOB etc. Using reflection, I'm adding columns to a new dataset based on the object properties:
For Each pi As PropertyInfo In person.GetType().GetProperties()
Dim column As New DataColumn(pi.Name, pi.PropertyType)
table.Columns.Add(column)
Next
My problem is that some of those properies are nullable types which aren't supported by datasets. Is there any way to extract the underlying system type from a nullable type?
Thanks.
Here's your answer, in VB. This may be overkill for your purposes, but it also might be useful to some other folks.
First off, here's the code to find out if you're dealing with a Nullable type:
Private Function IsNullableType(ByVal myType As Type) As Boolean
Return (myType.IsGenericType) AndAlso (myType.GetGenericTypeDefinition() Is GetType(Nullable(Of )))
End Function
Note the unusual syntax in the GetType. It's necessary. Just doing GetType(Nullable) as one of the commentors suggested did not work for me.
So, armed with that, you can do something like this... Here, in an ORM tool, I am trying to get values into a generic type that may or not be Nullable:
If (Not value Is Nothing) AndAlso IsNullableType(GetType(T)) Then
Dim UnderlyingType As Type = Nullable.GetUnderlyingType(GetType(T))
Me.InnerValue = Convert.ChangeType(value, UnderlyingType)
Else
Me.InnerValue = value
End If
Note that I check for Nothing in the first line because Convert.ChangeType will choke on it... You may not have that problem, but my situation is extremely open-ended.
Hopefully if I didn't answer your question directly, you can cannibalize this and get you where you need to go - but I just implemented this moments ago, and my tests are all passing.
Nullable.GetUnderylingType(myType)
will return the underlying type or null if it's not a nullable type.
I'm guessing that the problem is recognizing whether the property is nullable or not. In C# you do this with this code:
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
... but I'm not sure what the equivalent of that last clause is in VB.NET.
You can also use the GetGenericParameters() method on that type. myNullableObject.GetType().GetGenericParameters()[0] should give you the type of nullable it is (so Guid, Int32, etc.)
#Mendelt Siebenga: You can only call GetType on the value property if the variable is not set to null; otherwise, you'll get an exception.
What you want to do is use the "GetValueOrDefault" property and call GetType on that, since you are guaranteed it will not be null. Example:
Dim i As Nullable(Of Integer) = Nothing
Dim t As Type = i.GetValueOrDefault().GetType()