Getting a "Type Error" in vb.net code converted from C# - vb.net

I have this code in VB.Net 2010 and I am getting a "type Error" on New().
This code was converted from C#.
What am I doing wrong?
Public Function CredentialGet(ByVal sKey As String, ByRef sCred As String)
Dim sCredential As Element.Credential
sCredential = apiclient.SearchCredentials(sSoftwareKey, SessionID,
New() {New Element.SearchTerm() With {.FilterKey = "APK", .Value = sKey}})
sCred = sCredential.CredentialID
End Function

New() what? You are missing an object name there. You're now passing in an anonymous object.

Remove the parentheses for the anonymous type. VB.Net does not use them in that context, but looks for the With keyword instead. And functions should return a value. You don't return anything, so use a Sub:
Public Sub CredentialGet(ByVal Key As String, ByRef Cred As String)
Dim Credential As Element.Credential
Credential = apiclient.SearchCredentials(sSoftwareKey, SessionID, _
New With {New Element.SearchTerm() With {.FilterKey = "APK", .Value = Key}})
Cred = Credential.CredentialID
End Sub
I also question this design. It would be better to return a string:
Public Function CredentialGet(ByVal Key As String) As String
Return apiclient.SearchCredentials(sSoftwareKey, SessionID, _
New With {New Element.SearchTerm() With {.FilterKey = "APK", .Value = Key}}).CredentialID
End Function

Related

Custom function(s) in LINQ to Entities? How to write acceptable code?

Use of my simple function causes app to exit all the nested Using blocks and returns control to very outer End Using statement. But built-in function works fine. How to bypass this strange situation?
Public Shared ReadOnly GlobalSettingsKeyList As New HashSet(Of String)
Public Shared Function IsGlobalSetting(key As String) As Boolean
Return GlobalSettingsKeyList.Contains(key)
End Function
What doesn't work:
Using db = powerEntities.Open()
dim userID = 1
dim dbSettingsFound = (From setting In db.SETTINGS
Where setting.idUsers = If(IsGlobalSetting(setting.Name), Nothing, userID)
Where setting.Name.Contains(keyToMatch)) _
.ToDictionary(Function(x) x.Name, Function(y) y.Value)
End Using
What works fine:
Using db = powerEntities.Open()
dim userID = 1
dim dbSettingsFound = (From setting In db.SETTINGS
Where setting.idUsers = If(GlobalSettingsKeyList.Contains(setting.Name), Nothing, userID)
Where setting.Name.Contains(keyToMatch)) _
.ToDictionary(Function(x) x.Name, Function(y) y.Value)
End Using
{"LINQ to Entities does not recognize the method 'Boolean IsGlobalSetting(System.String)' method, and this method cannot be translated into a store expression."}
Done! Thanks to Jarekczek's answer here and his LinqExprHelper
So there is a way!
Private Shared Function GetDefaultKey(key As String) As String
If key.Contains("Station") Then
Return $"default{key.Substring("Station")}" 'my String Extension
Else
Return $"default{key}"
End If
End Function
Public Shared Function GetDefaultKeyAndCompareSetting(ByVal key As String) As Expression(Of Func(Of Settings, Boolean))
key = GetDefaultKey(key)
Return LinqExprHelper.NewExpr(Function(u As Settings) u.Name.Equals(key))
End Function
used like this...
Shared Sub Example(key As String)
...
Dim masterExpr = LinqExprHelper.NewExpr(Function(u As Settings, ByVal formatCompare As String) (formatCompare))
Dim isSameAsDefKey = masterExpr.ReplacePar("formatCompare", GetDefaultKeyAndCompareSetting(key).Body)
Dim result = (From def In db.Settings
Where def.idUsers Is Nothing).
Where(CType(isSameAsDefKey, Expression(Of Func(Of Settings, Boolean)))).FirstOrDefault
...
End Sub
...works like a charm

Why is this returning Index out of bound exception

When I get to the Read loop I get an index out of bounds error. I think its on the reader ordinal value, but I am not sure why I am getting it.
Private Function Create(Reader As SqlDataReader) As IEnumerable(Of MyObject)
SetOrdinals(MyObjectReader)
Dim MyObjects = New List(Of MyObject)
While MyObjectReader.Read()
Dim Temp = New MyObject() With {
.FirstValue = MyObjectReader.GetValue(Of Integer)(MyObjectReader(FirstValue_Ord)),
.SecondValue = If(MyObjectReader.GetValue(Of String)(MyObjectReader(SecondValue_Ord)), String.Empty).Trim(),
.ThirdValue = If(MyObjectReader.GetValue(Of String)(MyObjectReader(ThirdValue_Ord)), String.Empty).Trim(),
MyObjects.Add(Temp)
End While
Return MyObjects
End Function
Private Sub SetOrdinals(MyObjectReader As SqlDataReader)
FirstValueOrd = MyObjectReader.GetOrdinal("FirstValue")
SecondValue_Ord = MyObjectReader.GetOrdinal("SecondValue")
ThirdValue_Ord = MyObjectReader.GetOrdinal("ThirdValue")
End Sub
End Class
Public Module Extensions
<Extension>
Function GetValue(Of T)(rdr As SqlDataReader, i As Integer) As T
If rdr.IsDBNull(i) Then
Return Nothing
End If
Return DirectCast(rdr.GetValue(i), T)
End Function
End Module
You should just be passing in the ordinal to the GetValue calls:
While MyObjectReader.Read()
Dim Temp = New MyObject() With {
.FirstValue = MyObjectReader.GetValue(Of Integer)(FirstValue_Ord),
.SecondValue = If(MyObjectReader.GetValue(Of String)(SecondValue_Ord), String.Empty).Trim(),
.ThirdValue = If(MyObjectReader.GetValue(Of String)(ThirdValue_Ord), String.Empty).Trim()
}
MyObjects.Add(Temp)
End While
Here my version :)
Private Function Create(reader As SqlDataReader) As IEnumerable(Of MyObject)
Dim objects As New List(Of MyObject)()
Dim ordinals As New Ordinals(reader)
While reader.Read()
Dim Temp As New MyObject With
{
.FirstValue = reader.GetValueOrDefault(Of Integer)(ordinals.FirstValue),
.SecondValue = reader.GetValueOrDefault(ordinals.SecondValue, "").Trim(),
.ThirdValue = reader.GetValueOrDefault(ordinals.ThirdValue, "").Trim()
}
objects.Add(Temp)
End While
Return MyObjects
End Function
Private Class Ordinals
Public Property FirstValue As Integer
Public Property SecondValue As Integer
Public Property ThirdValue As Integer
Public Sub New(reader As SqlDataReader)
FirstValue = reader.GetOrdinal(nameOf(FirstValue))
SecondValue = reader.GetOrdinal(nameOf(SecondValue))
ThirdValue = reader.GetOrdinal(nameOf(ThirdValue))
End Sub
End Class
Public Module Extensions
<Extension>
Function GetValueOrDefault(Of T)(reader As SqlDataReader, ordinal As Integer) As T
Return reader.GetValueOrDefault(Of T)(ordinal, Nothing)
End Function
<Extension>
Function GetValueOrDefault(Of T)(reader As SqlDataReader,
ordinal As Integer,
defaultValue As T) As T
Dim value = reader(ordinal)
If value = DbNull.Value Then
Return defaultValue
End If
Return DirectCast(value, T)
End Function
End Module
Because extension method execute checking for DbNull.Value against already extracted object, we get rid from reading same value twice from SqlDataReader.
SqlDataReader.IsDbNull(index) reads value before checking for DbNull.
Extension method have two overloads:
- One which return default value of given type, if value is DbNull.Value. Nothing in vb.net is default value of type.
- And one which takes parameter for default value you want return if value is DbNull.Value. Possibility pass default value makes lines where you create new object shorter and more readable. We get rid of inline if statement.
Your extension method with name GetValue have "side effects". By name consumer of this method expect to get value from SqlDataReader. So he can expect to get DbNull.Value if database query return NULL, but instead he get null for string or 0 for integer. Name GetValueOrDefault is little bid more informative, so you don't need to go inside method to check what is doing.

How to write the contents of a dictionary to a MessageBox

In VB.NET I want to write the contents of a dictionary to a message box.
The dictionary is rather basic
Dim x As New Dictionary(Of Integer, Users)
x.Add("1", New Users("1", "Simon"))
The user class contains 2 attributes, user ID (Integer) and Username (String).
I am struggling to write the dictionary contents. I would like to write each dictionary entry to a string but i am having no success as I keep getting the error message:
Argument 'Prompt' cannot be converted to type 'String'.
You are passing a string where you specified an integer:
Fix:
Dim x As New Dictionary(Of Integer, Users)
x.Add(1, New Users(1, "Simon"))
Then to show the contents:
Dim sb As New StringBuilder
For Each item As KeyValuePair(Of Integer, Users) In x
sb.AppendLine(item.Key & ") " & item.Value.ToString)
Next
MessageBox.Show(sb.ToString())
Your Users class would need to override the ToString function or change the ToString call to the property in Users that shows the user's name.
Update to Users class:
Public Class Users
Private _p1 As Integer
Private _p2 As String
Sub New(ByVal p1 As Integer, ByVal p2 As String)
_p1 = p1
_p2 = p2
End Sub
Public Overrides Function ToString() As String
Return _p2
End Function
End Class
Here you go.
Dim sbMessage As New System.Text.StringBuilder(500)
For Each wKey As Integer In x.Keys
sbMessage.Append("Key = ").Append(wKey).Append(", Value = ").Append(x.Item(wKey).ToString()).AppendLine()
Next
MessageBox.Show(sbMessage.ToString)
To make this useful, you will need to override the ToString method in the Users class. For example, assuming that there is an ID and a name in this class:
Public Overrides Function ToString() As String
Dim sbText As New System.Text.StringBuilder(500)
sbText.Append("ID = ").Append(Me.Id).Append(", Name = ").Append(Me.Name)
Return sbText.ToString
End Function
For Each kvp As KeyValuePair(Of Integer, Users) In x
Console.WriteLine("Key = {0}, Value = {1}", _
kvp.Key, kvp.Value)
Next kvp
Something like that, bearing in mind that your Value will be a Users object, and that as #LarsTech said, you should pass in an integer instead of a string into the Dictionary

How to convert a string of key/value pairs to HashTable or Dictionary or?

In VB.NET, how can I convert the following string into some kind of key/value type such as a Hashtable, Dictionary, etc?
"Name=Fred;Birthday=19-June-1906;ID=12345"
I want to extract Birthday or ID without having to split the string into an array.
EDIT: I'd prefer not to split the string into an array in case the format of the string changes later. I don't have control over the string. What if someone switches the order around or adds another element?
I’m currently unable to test this, lacking a VB compiler, but the following solution should also work, and it has the advantage of not requiring an explicit loop. It uses the Linq method ToDictionary and two nested Split operations:
Dim s = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim d = s.Split(";"c).Select(Function (kvp) kvp.Split("="c)) _
.ToDictionary( _
Function (kvp) kvp(0), _
Function (kvp) kvp(1))
First, we split on the outer delimiter (i.e. the semi-colon). From the resulting array, we select by splitting again, this time on =. The resulting array of arrays is converted to a dictionary by specifying that the first item is to become the key and the second is to become the value (the identifier kvp stands for “key-value pair”).
Since I can’t check the exact VB syntax and the above may contain subtle errors, here is the equivalent C# code (tested for correctness):
var s = "Name=Fred;Birthday=19-June-1906;ID=12345";
var d = s.Split(';').Select(kvp => kvp.Split('='))
.ToDictionary(kvp => kvp[0], kvp => kvp[1]);
Not sure why you don't want to split it. If you're sure there won't be any extra = or ; then you could just do:
Dim s As String = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim d As New Dictionary(Of String, String)
For Each temp As String In s.Split(";"c)
Dim index As Int32 = temp.IndexOf("="c)
d.Add(temp.Substring(0, index), temp.Substring(index + 1))
Next
Which might not be beautiful, but is very easy to understand.
input.Split(";"c) returns an array of key/value:
{ "Name=Fred", "Birthday=19-June-1906" , "ID=12345" }
so pair.Split("="c) returns { "Name", "Fred" } etc
If you want an alternative to doing a String.Split; there is always Regular Expressions as an alternative:
Dim map As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim match As Match = Regex.Match("Name=Fred;Birthday=19-June-1906;ID=12345", "(?<Name>[^=]*)=(?<Value>[^;]*);?")
While (match.Success)
map.Add(match.Groups("Name").Value, match.Groups("Value").Value)
match = match.NextMatch()
End While
The regular expression itself could be beefed up to better handle whitespace between key/value's and pair's but you hopefully get the idea. This should only pass through the string once to build up a string dictionary of keys and values.
Dim persSeparator as string=";"
Dim keyValSeparator as string="=";
Dim allPersons As New Dictionary(Of String, Person)
Dim str As String = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim parts As New List(Of String)(str.Split(persSeparator.ToCharArray)) 'why dont want you to split this string??
Dim person As New Person
For Each part As String In parts
Dim keyValue() As String = part.Split(keyValSeparator.toCharArray())
Select Case keyValue(0).ToUpper
Case "ID"
person.ID = keyValue(1)
Case "NAME"
person.Name = keyValue(1)
Case "BIRTHDAY"
person.BirthDay= keyValue(1)
End Select
Next
If Not allPersons.ContainsKey(person.ID) Then
allPersons.Add(person.ID, person)
End If
Public Class Person
Private _name As String
Private _birthday As String
Private _id As String = String.Empty
Public Sub New()
End Sub
Public Sub New(ByVal id As String)
Me._id = id
End Sub
Public Sub New(ByVal id As String, ByVal name As String)
Me._id = id
Me._name = name
End Sub
Public Sub New(ByVal id As String, ByVal name As String, ByVal birthday As String)
Me._id = id
Me._name = name
Me._birthday = birthday
End Sub
Public Property ID() As String
Get
Return Me._id
End Get
Set(ByVal value As String)
Me._id = value
End Set
End Property
Public Property Name() As String
Get
Return Me._name
End Get
Set(ByVal value As String)
Me._name = value
End Set
End Property
Public Property BirthDay() As String
Get
Return Me._birthday
End Get
Set(ByVal value As String)
Me._birthday = value
End Set
End Property
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If TypeOf obj Is Person AndAlso Not obj Is Nothing Then
Return String.Compare(Me._id, DirectCast(obj, Person).ID) = 0
Else : Return False
End If
End Function
End Class
If you were just wanting to extract the birthday and ID from the string and place as a value pair in some sort of dictionary, for simplicity I would use regular expressions and then a generic dictionary (of string, valuepair structure). Something like this:
Imports System.Text.RegularExpressions
Imports System.Collections.Generic
Sub Main()
Dim Person As New Dictionary(Of String, ValuePair)
Dim s As String = "Name=Fred;Birthday=19-June-1906;ID=12"
Dim r As Regex = New Regex("Name=(.*);Birthday=(.*);ID=(.*$)")
Dim m As Match = r.Match(s)
Person.Add(CStr(m.Groups(1).Value), _
New ValuePair(CDate(m.Groups(2).Value), CInt(m.Groups(3).Value)))
Console.WriteLine(Person("Fred").Birthday.ToString)
Console.WriteLine(Person("Fred").ID.ToString)
Console.Read()
End Sub
Friend Structure ValuePair
Private _birthday As Date
Private _ID As Int32
Public ReadOnly Property ID() As Int32
Get
Return _ID
End Get
End Property
Public ReadOnly Property Birthday() As Date
Get
Return _birthday
End Get
End Property
Sub New(ByVal Birthday As Date, ByVal ID As Int32)
_birthday = Birthday
_ID = ID
End Sub
End Structure

VB.NET Generic Function

What I want to do is, based on the type of T do different opperations. Below is a simple example of my problem.
Public Shared Function Example(Of T)() As T
Dim retval As T
If TypeOf retval Is String Then
Dim myString As String = "Hello"
retval = myString
ElseIf TypeOf retval Is Integer Then
Dim myInt As Integer = 101
retval = myInt
End If
Return retval
End Function
I get the error "Value of Type 'String' Cannot be converted to 'T'" Same with the integer part. If I cast either to an object before asigning them to retval it works but I think that would defeat my purpose and be less efficient. Any Ideas? Thanks!
It's probably a bit late, but try this:
Public Shared Function CAnyType(Of T)(ByRef UTO As Object) As T
Return CType(UTO, T)
End Function
Public Shared Function ExecuteSQLstmtScalar(Of T)(ByVal strSQL As String) As T
Dim T_ReturnValue As T
' Here we have the result of a DB query '
Dim obj As Object = "Value from DB query cmd.ExecuteScalar"
Dim strReturnValue As Object = obj.ToString();
Try
Dim tReturnType As Type = GetType(T)
If tReturnType Is GetType(String) Then
Return CAnyType(Of T)(strReturnValue)
ElseIf tReturnType Is GetType(Boolean) Then
Dim bReturnValue As Boolean = Boolean.Parse(strReturnValue)
Return CAnyType(Of T)(bReturnValue)
ElseIf tReturnType Is GetType(Integer) Then
Dim iReturnValue As Integer = Integer.Parse(strReturnValue)
Return CAnyType(Of T)(iReturnValue)
ElseIf tReturnType Is GetType(Long) Then
Dim lngReturnValue As Long = Long.Parse(strReturnValue)
Return CAnyType(Of T)(lngReturnValue)
Else
MsgBox("ExecuteSQLstmtScalar(Of T): This type is not yet defined.")
End If
Catch ex As Exception
End Try
Return Nothing
End Function
(the secrect is casting your generic result to object, then casting from type Object to template type T).
PS:
You are responsible to ensure that your code works correctly with nullable types and NOT nullable types, as well as System.DbNull.Value. For example when string is NULL and return value type is Boolean (not nullable). On a sidenote, please also note that VB Nothing is NOT equal NULL, it's equal to C#'s default(T) (e.g. System.Guid.Empty for Guid)
With a generic method, T will be of exactly one type each time. Let's say that you have code calling Example(Of Integer). Now, in your mind, replace T with Integer. The resulting method will contain these lines (amongst others).
Dim retval As Integer
If TypeOf retval Is String Then
Dim myString As String = "Hello"
retval = myString
' more code follows '
Assigning a String to an integer like that will never work. Sure, that code will also never execute, since the If-block prevents that, but the code will still not compile. (As a side not, the above code will fail to compile because the TypeOf keyword is restricted to use with reference types, but that is another story)
Typically when creating generic methods, you will want to do the same thing with whatever input you get, but in a type safe manner. If you want to have different behavior for different types of input, you are usually better off by overloading the methods instead.
retVal = (T) "Hello World!"
Do retval = Ctype(Mystring, T) or retVal = Ctype(MyInt, T)
An alternative solution is encapsulate this kind of logic in a class and use VB CallByName function:
Class Aux(Of T)
Public Value As T
Private dicc As Dictionary(Of String, Object)
Sub New()
dicc = New Dictionary(Of String, Object)
dicc.Add("system.string", "hola")
dicc.Add("system.int32", 15)
dicc.Add("system.double", 15.0)
End Sub
Public Function Test() As T
Dim typeName As String = GetType(T).ToString.ToLower
If dicc.ContainsKey(typeName) Then
CallByName(Me, "Value", CallType.Set, dicc(typeName))
End If
Return Value
End Function
Protected Overrides Sub Finalize()
MyBase.Finalize()
If Not (dicc Is Nothing) Then dicc.Clear()
dicc = Nothing
End Sub
End Class