Passing lists from WCF to WCF in VB - vb.net

I'm trying to populate one list from another. I would think this code should work, but at the end of the day I get a list of identical items.
Public Sub WriteDatFile(ByRef lstReasons As System.Collections.Generic.List(Of LetterReason))
Dim tmplstReason As New TCPService.LetterReason
Dim tmplstReasons As New System.Collections.Generic.List(Of TCPService.LetterReason)
'Load the letter reasons
For Each LetterReason In lstReasons
tmplstReason._reason = LetterReason.Reason
tmplstReasons.Add(tmplstReason)
Next
RetVal = .......
End Sub
Now, when I set a breakpoint and check from the calling WCF I get this:
lstReason(0).Reason = One
lstReason(1).Reason = Two
lstReason(2).Reason = Three
But, when I set a breakpoint (after the load) in this subroutine I get the following output:
tmplstReason(0)._reason = Three
tmplstReason(0)._reason = Three
tmplstReason(0)._reason = Three
What's going on??? Any ideas?
Thanks,
Jason

You need to create a new instance of LetterReason inside the loop and add the new instance to the list. Try this
For Each LetterReason In lstReasons
Dim tmplstReason As New TCPService.LetterReason
tmplstReason._reason = LetterReason.Reason
tmplstReasons.Add(tmplstReason)
Next

tmplstReason._reason = LetterReason.Reason
tmplstReasons.Add(tmplstReason)
Look carefully. You're not actually changing tmplstReason, you're changing the ._reason property of it. You then add tmplstReason to the list 3 times.
The result is that you actually add the same thing to the list each time, and change that one object's ._reason variable each time. Since they're all the same, they all have the same value. :)

Related

Return values from Dictionary key

I'm a bit of a vb.net noob - apologies if this is a silly question.
I have a collection named Applications and these store objects of type application.
Dim Applications As New Dictionary(Of String, Application)()
Each application is created and added to this collection using
Public Sub New(dbid As String, name As String, status As String, mode As String)
csv_dbid = dbid
csv_app = name
csv_status = status
csv_mode = mode
End Sub
See the image i've included which shows in the debug/output my collection created with the objects and values associated.
I want to know a way I can access a Key and return all the corresponding values of csv_dbid, csv_app, csv_status and csv_mode. I've been googling for a bit and struggling.
Many thanks in advance.
Gary Waddell
In the case of the posted screenshot you would do:
Dim app = Applications("100")
app Will be a HealthCareApplication like any other so you can just use it as you would:
app.csv_dbid = "123"
You can also just refer to the dictionary item without a variable:
Applications("100").csv_dbid = "123"
To find out if the dictionary knows of a key, use the ContainsKey method. This can be particularly useful if you're iterating a colection and want to handle duplicates:
For Each thing in someList
if Applications.ContainsKey(thing.Key) Then
HandleDuplicate(thing)
Else
Applications(thing.Key) = New HealthCareApplication
End if
'It certainly exists in the collection now and this won't crash with KeyNotFound
Applications(thing.Key).csv_dbid = thing.DbId
Next thing
If you enumerate a dictionary you get a collection of KeyValuePair, the Key is (in this case) the "100" you used as the indexer. The Value is a HealthCareApplication type
For Each kvp in Applications
console.Write(kvp.Key)
Console.Write(kvp.Value.csv_dbid)
Next kvp

Combining two list, only records with 1 specific unique property

I'm combining two lists in visual basic. These lists are of a custom object. The only record I want to combine, are the once with a property doesn't match with any other object in the list so far. I've got it running. However, the first list is just 1.247 records. The second list however, is just short of 27.000.000 records. The last time I successfully merged the two list with this restriction, it took over 5 hours.
Usually I code in C#. I've had a similar problem there once, and solved it with the any function. It worked perfectly and really fast. So as you can see in the code, I tried that here too. However it takes way too long.
Private Function combineLists(list As List(Of Record), childrenlist As List(Of Record)) As List(Of Record) 'list is about 1.250 entries, childrenlist about 27.000.000
For Each r As Record In childrenlist
Dim dublicate As Boolean = list.Any(Function(record) record.materiaalnummerInfo = r.materiaalnummerInfo)
If Not dublicate Then
list.Add(r)
End If
Next
Return list
End Function
The object Record looks like this ( I wasn't sure how to make a custom object in VB, and this looks bad, but it worked):
Public Class Record
Dim materiaalnummer As String
Dim type As String 'Config or prefered
Dim materiaalstatus As String
Dim children As New List(Of String)
Public Property materiaalnummerInfo()
Get
Return materiaalnummer
End Get
Set(value)
materiaalnummer = value
End Set
End Property
Public Property typeInfo()
Get
Return type
End Get
Set(value)
type = value
End Set
End Property
Public Property materiaalstatusInfo()
Get
Return materiaalstatus
End Get
Set(value)
materiaalstatus = value
End Set
End Property
Public Property childrenInfo()
Get
Return children
End Get
Set(value)
children = value
End Set
End Property
End Class
I was hoping that someone could point me in the right direction to shorten the time needed. Thank you in advance.
I'm not 100% sure what you want the output to be such as all differences or just ones from the larger list etc but I would definitely try do it with LINQ! Basically sql for vb.net data so would something similar to this:
Dim differenceQuery = list.Except(childrenlist)
Console.WriteLine("The following lines are in list but not childrenlist")
' Execute the query.
For Each name As String In differenceQuery
Console.WriteLine(name)
Next
Also side-note i would suggest not calling one of the lists "list" as it is bad practice and is a in use name on the vb.net system
EDIT
Please try this then let me know what results come back.
Private Function combineLists(list As List(Of Record), childrenlist As List(Of Record)) As List(Of Record) 'list is about 1.250 entries, childrenlist about 27.000.000
list.AddRange(childrenlist) 'combines both lists
Dim result = From v In list Select v.materiaalnummerInfo Distinct.ToList
'result hopefully may be a list with all distinct values.
End Function
Or Don't combine them if you dont want to.

How to use reflection for accessing object from its name as a string

Ive a folder monitoring application where around 25 filewatchers monitoring 25 folders. Each of the filewatchers named fsw1,fsw2 ....
bCreateFileCheck = True
fsw1 = New FileSystemWatcher(My.Settings.UserRootFolder1)
fsw1.IncludeSubdirectories = True
fsw1.EnableRaisingEvents = True
fsw1.NotifyFilter = (NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName)
So this is repeating for the 25 folders, but only difference is name changing of fsw1 to fsw2,fsw3 etc. and also My.Settings.UserRootFolder1 to My.Settings.UserRootFolder2,My.Settings.UserRootFolder3 etc.
So how can we achieve this using for loop without writing individual code blocks for every filewatchers. I guess using some reflection techniques it can be achieved.
Don't make your life harder than it needed to be. Use an array (or List(Of T) if you need something flexible):
Dim watchers(24) As FileSystemWatcher
For i As Integer = 0 To watchers.GetUpperBound(0)
Dim path = CStr(My.Settings.Item("UserRootFolder" & (i + 1)))
watchers(i) = New FileSystemWatcher(path)
'Do further initialization...
Next
If the structure is fixed and you cannot really change it, you can set the variables to the objects that you created in the For loop. So change the loop as follows:
'...
Dim watcher = New FileSystemWatcher(...)
Me.GetType().GetField("fsw" & (i + 1)).SetValue(Me, watcher)
This gets the field with the appropriate name and sets its value to the object that you just created (I assume that it is a field based on its naming).

Why won't this list of struct allow me to assign values to the field?

Public Structure testStruct
Dim blah as integer
Dim foo as string
Dim bar as double
End Structure
'in another file ....
Public Function blahFooBar() as Boolean
Dim tStrList as List (Of testStruct) = new List (Of testStruct)
For i as integer = 0 To 10
tStrList.Add(new testStruct)
tStrList.Item(i).blah = 1
tStrList.Item(i).foo = "Why won't I work?"
tStrList.Item(i).bar = 100.100
'last 3 lines give me error below
Next
return True
End Function
The error I get is: Expression is a value and therefore cannot be the target of an assignment.
Why?
I second the opinion to use a class rather than a struct.
The reason you are having difficulty is that your struct is a value type. When you access the instance of the value type in the list, you get a copy of the value. You are then attempting to change the value of the copy, which results in the error. If you had used a class, then your code would have worked as written.
try the following in your For loop:
Dim tmp As New testStruct()
tmp.blah = 1
tmp.foo = "Why won't I work?"
tmp.bar = 100.100
tStrList.Add(tmp)
Looking into this I think it has something to do with the way .NET copies the struct when you access it via the List(of t).
More information is available here.
Try creating the object first as
Dim X = New testStruct
and setting the properties on THAT as in
testStruct.blah = "fiddlesticks"
BEFORE adding it to the list.

Update columns in one LINQ object with another LINQ object

I have two LINQ objects which have exactly the same columns and I would like to be able to update one with the fields from the other. I first create a new object from some data in a file, then I query the database for an existing item with the same ID. What I would like to be able to do is update the existing objects details with the newly created objects details.
So far the way I have been doing it is to list all the columns and update them manually but as you can see this can cause maintenance headaches.
With OldCaller
.ADDRESS = NewCaller.ADDRESS
.COMPANY = NewCaller.COMPANY
.CONTACT_HOURS = NewCaller.CONTACT_HOURS
.CONTACT_NAME = NewCaller.CONTACT_NAME
.CUSTOMER_ID = NewCaller.CUSTOMER_ID
.EMAIL_ADDRESS = NewCaller.EMAIL_ADDRESS
.FAX_NUMBER = NewCaller.FAX_NUMBER
.FAX_TYPE = NewCaller.FAX_TYPE
.MOBILE = NewCaller.MOBILE
.POSTCODE = NewCaller.POSTCODE
.PUBLIC_ADDRESS = NewCaller.PUBLIC_ADDRESS
.PUBLIC_TELEPHONE = NewCaller.PUBLIC_TELEPHONE
.STATE = NewCaller.STATE
.SUBURB = NewCaller.SUBURB
.TELEPHONE = NewCaller.TELEPHONE
End With
I would like to be able to find a way to clean this up a bit. Does anyone know of a better way to do what I need.
I do this sort of thing when I create an instance of an object from a template. Basically I have a method that iterates over the public properties of the template, finds the corresponding property in the object being created, and invokes the property setter on the new object, all via reflection.
I haven't tested this yet but this is what I have come up with.
Dim _OldCallerProperties = OldCaller.GetType().GetProperties(Reflection.BindingFlags.Public)
Dim _NewCallerProperties = NewCaller.GetType.GetProperties(Reflection.BindingFlags.Public)
For Each Prop In _OldCallerProperties
Dim _matchingProperty = _NewCallerProperties.Where(Function(p) p.Name = Prop.Name).FirstOrDefault
Dim _newvalue = _matchingProperty.GetValue(_matchingProperty, Nothing)
Prop.SetValue(Prop, _newvalue, Nothing)
Next
Like I said I haven't tested it but I'm sure it should work.