How to parameterise a object member in VB.NET for WSDL - vb.net

I have some code that populates a SOAP request in VB.NET. I get the data from a SQL query and run through the objects members to populate each one. I'm trying to find a quick(ish) way to not provide the member when it is empty rather than checking each value before applying it. As the SQL data comes in from a pipe delimited file in the first place we never get NULL just empty cells which get sent in the SOAP request as "".
Is there a way to define an objects member using a variable rather than its literal name?
UpLB_request_Items.Spare8 = Convert.ToString(SourceDataSet.tables(0).Rows(i)(colSPARE8))
UpLB_request_Items.Spare9 = Convert.ToString(SourceDataSet.tables(0).Rows(i)(colSPARE9))
UpLB_request_Items.Spare10 = Convert.ToString(SourceDataSet.tables(0).Rows(i)(colSPARE10))
Call IterateObject(UpLB_request_Items)
So this populates each member with the value from the SQL data (SourceDataSet), then I could send the completed object down to a Sub to check each value before I actually send it to the webservice
Sub IterateObject(objName)
Dim CollName = ""
For Each m As System.Reflection.PropertyInfo In objName.GetType().GetProperties()
If m.CanRead Then
If m.PropertyType.Name = "String" Then
CollName = m.Name
Dim CollVal = CallByName(objName, CollName, CallType.Get)
If CollVal = "" Then
objName.CollName = Nothing 'this is the tricky bit
End If
End If
End If
Next
End Sub
The bit where it obviously breaks is objName.CollName = Nothing as it will then say that the object doesn't have a member called CollName and I'm not sure if there is a way to force the code to evaluate CollName to its value (e.g. SPARE8 rather than the string "CollName")

I went with this in the end
UpLB_request_Items.Spare4 = Strings.Replace(Convert.ToString(SourceDataSet.Tables(0).Rows(i)(colSPARE4)), "", Nothing)

Related

VB.Net | Is there a way to reference a dynamic amount of variables as arguments to function/sub?

I'm trying to pass a dynamic amount of variables to a Sub by using ByRef;
Essentially I'm trying to create a module that I can easily import into my projects and make handling the file saving/loading process automated.
The Sub/Function would take a number of variables as references and then loop through them changing each one's value.
I realize I'm missing a crucial point in how visual basic's syntax works but I haven't been able to figure out what I need to do.
The code I've written for this is:
Public Sub LoadSaveToVars(ByRef KeyNamesAndVars() As Object, ByVal FileLoc As String = "")
If isEven(KeyNamesAndVars.Length) Then
Dim Contents As String = My.Computer.FileSystem.ReadAllText(FileLoc)
Dim isOnName As Boolean = True
Dim CurrentVal As String = ""
For i = 0 To KeyNamesAndVars.Length - 1
If isOnName Then
CurrentVal = GetStringValue(KeyNamesAndVars(i), Contents) 'Get the value of the key with the key name in the array
isOnName = False
Else
KeyNamesAndVars(i) = CurrentVal 'Set the variable referenced in the array to the value
isOnName = True
End If
Next
Else
Throw New ArgumentOutOfRangeException("The key names and variables supplied are not even.", "Error loading to variables!")
End If
End Sub
And here's how I try to use this function:
Dim TestVar1 As String = ""
Dim TestVar2 As String = ""
LoadSaveToVars({"key1", TestVar1, "key2", TestVar2})
To keep this question clean I did not include the other functions, but I did make a poor attempt at drawing what I want to happen: https://gyazo.com/eee34b8dff766401f73772bb0fef981a
In the end, I want TestVar1 to be equal to "val1" and TestVar2 to be equal to "val2" and to be able to extend this to a dynamic number of variables. Is this possible?

Pass a variable in as a member name to an Object?

Not sure if this is even possible but I need to be able to pass a variable's value in as an object's member name.
Basically I'm using a wdsl that has a number of objects where some of them could contain a collection, I need to make data grids to show the data in the collection which is straightforward enough but at the moment I have to make code for each object/collection that defines how many effective columns and their names and types.
This works fine albeit a bit long winded but it will also break if the wdsl changes and the objects collection content changes (names, types etc.)
What I need is to be able to pass a object name to a sub which will work out if the object contains a collection (PropertyType will contain []), read its name and pass that name down to a loop which will go through at the correct level to retrieve the "column" names and data types.
I have got almost all of this working until I want to pass the collection name into a loop as an object member name as it obviously doesn't evaluate the string value of CollName in the below example, it will just error saying CollName isn't a member of the object which of course it isn't but the variables actual value would be.
Sub IterateObject(objName)
Dim CollName = ""
For Each m As System.Reflection.PropertyInfo In objName.GetType().GetProperties()
If m.CanRead Then
If InStr(m.PropertyType.ToString, "[]") <> 0 Then
CollName = m.Name
End If
End If
Next
For Each p As System.Reflection.PropertyInfo In objName.CollName(2).GetType().GetProperties()
If p.CanRead Then
If p.Name <> "ExtensionData" Then
MsgBox(p.Name & " - " & (p.PropertyType.ToString))
End If
End If
Next
End Sub
Is there a way of doing effectively objName.(value of CollName)(2).GetType().GetProperties()
This seems to have fixed it using CallByName to allow me to make a new object out of just the bit I need
Sub IterateObject(objName)
Dim CollName = ""
For Each m As System.Reflection.PropertyInfo In objName.GetType().GetProperties()
If m.CanRead Then
If InStr(m.PropertyType.ToString, "[]") <> 0 Then
CollName = m.Name
End If
End If
Next
Dim CollObj
CollObj = CallByName(objName, CollName, CallType.Get)
For Each p As System.Reflection.PropertyInfo In CollObj(0).GetType().GetProperties()
If p.CanRead Then
If p.Name <> "ExtensionData" Then
MsgBox(p.Name & " - " & (p.PropertyType.ToString))
End If
End If
Next
End Sub

Values in list of structures don't change

I have defined a structure in my code and have a list of this structures"
Structure Parcel
Public name As String
Public type As String
End Structure
Dim ParcelList As New List(Of Parcel)
Then I'm trying to set some values to an element of the list which name is known to me
For Each myParcel As Parcel In ParcelList
If (myParcel.name = "Parcel1") Then
myParcel.type="Type1"
End If
Next
Unfortunately values in my list don't change at all. what am I doing wrong?
As Parcel is a Structure, it is passed by value so when iterating through collection, you are modifying a copy of your structure.
To better understand this case, you should understand what For Each really is. Your code can be translated into:
Dim enumerator As List(Of Parcel).Enumerator = ParcelList.GetEnumerator()
While enumerator.MoveNext()
' Here you have a local copy of your Structure
Dim myParcel As Parcel = enumerator.Current
Dim flag As Boolean = Operators.CompareString(myParcel.name, "Parcel1", False) = 0
If flag Then
' Here you modify your local copy
myParcel.type = "Type1"
End If
End While
If Parcel was a Class, it would be passed by reference so no local copy would be created and line myParcel.type = "Type1" would change proper object existing in your collection.
As already Stated this is because you are modifying a local copy of a value type. One way round this is to access the items in the list by ordinal and replace the ordinal value type with a new type:
For i As Integer = 0 To ParcelList.Count - 1
If ParcelList(i).name = "Parcel1" Then
ParcelList(i) = New Parcel With {.name = ParcelList(i).name, .type = "Type1"}
End If
Next
But really you should change the Sturcture to a Class
When checking for strings use Equals instead of '='.
If (myParcel.name.equals("Parcel1")) Then
myParcel.type="Type1"
End If
Strings are in fact 'Objects'. When you compare Strings (example StringA = StringB), you check the allocation of the String in Memory instead of the contents of the string.
Even better would be:
If (myParcel.name.ToUpper().equals(Cstr("Parcel1").toUpper())) Then
myParcel.type="Type1"
End If
That way you ignore any difference case-wise.
example:
myParcel.name = "teST"
myParcel.name.equals("test")
is False
myParcel.name.ToUpper().equals(Cstr("test").toUpper())
is true

Exception has been thrown by the target of an invocation

I know this is a relatively common error but I am working in an application that allows custom reports to be written using vb.net or C# for scripting. Error handling is very poor and I am not knowledgable enough to add my own (if it is even possible).
My code simply retrieves a value that is stored in a textbox on the report formatted as LastName, FirstName and truncates all characters after the comma. This value LastName is placed in a new textbox on the report. Here is my code:
Sub Detail1_Format
Dim lastNameFirstName As String = ""
Dim lastName As String = ""
Dim lastNameCommaIndex As Integer=
'set lastNameFirstName value from data on report'
lastNameFirstName = ReportUtilities.ReturnTextBoxValue(rpt,"Detail1","txtdtr_DTOOLS_CompanyName")
'find index of first comma in lastNameFirstName string'
lastNameCommaIndex = lastNameFirstName.IndexOf(",")
'set contents of lastName using substring of lastNameFirstString'
lastName = lastNameFirstName.SubString(0, lastNameCommaIndex)
'the error happens above when I use lastNameCommaIndex'
'to set the number of characters in my substring'
'set client name textbox value using lastName'
ReportUtilities.SetTextBoxValue(rpt,"Detail1","txtdtr_CALC_ClientName",lastName)
End Sub
The error happens when I use lastNameCommaIndex to set the number of characters in my substring. If I replace it with a number the report is published properly.
You might want to use string.split it will make this a lot easier
e.g.
If Not String.IsNullOrWhiteSpace(lastNameFirstName) Then
lastName = lastNameFirstName.Split(",".ToCharArray())(0)
End If
Here's how you write IsNullOrWriteSpace if you don't have .net 4.0
Function IsNullOrWhiteSpace(ByVal s As String) As Boolean
If s Is Nothing Then
Return True
End If
Return s.Trim() = String.Empty
End Function

Excel VBA - Initializing Empty User Types and Detecting Nulls

I have created a user defined type to contain some data that I will use to populate my form. I am utilizing an array of that user defined type, and I resize that array as I pull data from an off-site server.
In order to make my program easier to digest, I have started to split it into subroutines. However, when my program is initialized, I cannot tell when a particular array has been initialized, and so I cannot be certain that I can call a size function to see if the array is empty.
Is there a way to initialize an empty user type or detect a null user type? Currently, I am hard-coding it in and I would prefer a more elegant solution.
In addition to isempty(array) solution -
If IsNull(array) then
msgbox "array is empty"
End If
AFAIK, you cannot check whether user-defined type was initialized before it was sent as an argument to a procedure/function.
I am quoting this example from VBA help
Type StateData
CityCode(1 To 100) As Integer ' Declare a static array.
County As String * 30
End Type
The County field is initialized to some value, which you can use a base value.
If the user sets this field explicitly, it means it holds some value & remains uninitialized, otherwise.
for e.g.
Sub main()
Dim example As StateData
MsgBox IsInitialized(example)
Dim example2 As StateData
example2.County = "LA"
MsgBox IsInitialized(example2)
End Sub
Function IsInitialized(arg As StateData) As Boolean
Dim initCounty As String * 30
IsInitialized = (arg.County <> initCounty)
End Function
Try:
dim v
if isempty(v) then
msgbox "is empty"
end if
If myObjectVariable is Nothing
should work to detect if an object has been initialized.
Edit: "is nothing" DOES work, if it is an object variable:
Dim blah As Object
If blah Is Nothing Then
MsgBox "blah is nothing!"
End If
Dim foo as variant
If IsEmpty(foo) Then
MsgBox "foo is empty!"
End If
If you need to check whether the whole dynamic array of custom types was initialized or not (not just particular element) in VBA, then this might not be possible directly (as none of the IsEmpty etc. functions works on custom types). However you might be able to easily restructure your program to return an array of custom types of size 0 to indicate that nothing was read/initialized.
Private Function doStuff() As customType()
Dim result() As customType
' immediately size it to 0 and assing it as result
ReDim result(0)
doStuff = vysledek
' do real stuff, ... premature "Exit Function" will return an array of size 0
' possibly return initialized values
End Function
' then you can all
If (UBound(tabulky) = 0) Then
MsgBox "Nope, it is not initialized."
End If