Dictionary comes back from cache as Null/Nothing - vb.net

I'm writing some vb.net code to retrieve some data from middleware, do some stuff with it, and then save a dictionary of name/value pairs to cache for quick retrieval when needed.
It all seems to go well until I retrieve the object from cache and it is always Null/Nothing.
Yet in the watch window I can see my cached object and it has data in it.
Thinking maybe it's a serialization issue I made a new class that inherits Dictionary and is marked as serializable but I still get the same results.
So right now I have this class:
<Serializable()> Public Class SerialDict
Inherits Dictionary(Of String, String)
Public Sub New()
End Sub
End Class
I populate it and put it into cache like this:
Dim Licenses As New SerialDict
For Each r As DataRow In dtLicenses.Rows
Dim prikey As String = r("SettingID").ToString.Trim
Dim decryptionKey As String = GetHash((xx))
Dim licData As String = DecryptData(r("SettingVal"), decryptionKey)
Licenses.Add(r("SettingKey"), licData)
Next
If IsNothing(HttpContext.Current.Cache("Licenses")) Then
HttpContext.Current.Cache.Add("Licences", Licenses, Nothing, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, Nothing)
End If
Then elsewhere we need to check that data so I try to retrieve it like this:
Dim Licences As SerialDict = CType(HttpContext.Current.Cache("Licenses"), SerialDict)
At this point Licenses is always Nothing, but the watch window shows data in HttpContext.Current.Cache("Licenses").
Any suggestions? Thanks!

Related

VB store list of records in a text file and read back

I have a VB program in VS2017. It reads many excel files and store relevant data in
Dim total_sessions As New List(Of Session_CSV_file)
The list is big (~10k), and includes about 20 fields each.
Is there a way in VB to store it in a file in one command and read it later on easily., without reading and writing each field?
Often I need to debug the later part. I prefer doing it without rerunning the whole first part.
Or is there a way in Visual Studio, to stop the run at a certain point, and store this point in a dump file, like simulators often do? (so they can run the simulation from this point later on)
Perhaps if you load your csv data into a DataTable instead:
Dim dt as New DataTable
dt.Columns.Add("Name")
dt.Columns.Add("Age", GetType(Int32))
For Each filepath in tenKFilePaths
For Each line in File.ReadAllLines(file)
Dim bits = line.Split(","c)
dt.Rows.Add(bits(0), Convert.ToInt32(bit(1))
Next line
Next filepath
Then you can save the whole lot to xml:
dt.WriteXml("c:\temp\my.xml")
Have a coffee, reboot etc the read them again and carry on where you left off:
Dim dt2 as New DataTable
dt2.ReadXml("c:\temp\my.xml")
Raw datatables are a bit difficult to work with because you end up accessing their rows and column data by string indexing and having to cast it a lot - fairly lame. There is a better way to use them, as visual studio can create custom classes that inherit from the base datatables and offer lots of functionality that does away with all the awkwardness of dealing with datatables as a very generic thing. Just like the forms designer creates a UI form that inherits from Form, the dataset designer creates custom datatables that inherit from the base
If you add a new DataSet type object to your project, double click it it opens something like a database designer. Right click that surface and choose Add DataTable, Add your Columns with their respective datatypes etc - this is the equivalent of your Session_Csv_File class, with its properties and fields. Double click on it and you can add code etc - if your session_csv_file has custom methods you can transfer their code to the custom data row
Imagine I created a new datatable in a DataSet and called it SessionCsvFiles. Imagine I added a Name and Age column, and I added a custom method called BlahBlah
I'd then be able to say in code things like:
Dim dt as New MyCustomDataSet.SessionCsvFilesDataTable
'it's like making a new SessionCsvFile
Dim r = dt.NewSessionCsvFilesRow()
'It's like setting your properties
r.Name = csvBits(0)
r.Age = Convert.ToInt32(csvBits(1))
'It's like adding your file to the List
dt.Add(r)
You now have virtually the same thing as you had before - a collection of objects that represents things about your csv files. The difference is with this route it has an already-built-in way of saving all the properties and the collection to xml and reading it back again
Add the Serializable attribute to the Class. I added a parameterized constructor for ease in building my list but you must provide a constructor without parameters for this to work. Thus, the empty Sub New.
<Serializable()>
Public Class Session_CSV_file
Public Property ID As Integer
Public Property Name As String
Public Property Type As String
Public Sub New()
End Sub
Public Sub New(SessID As Integer, SessName As String, SessType As String)
ID = SessID
Name = SessName
Type = SessType
End Sub
End Class
The add Imports System.Xml.Serialization to the top of the file.
Private Sub SaveList()
Dim serializer = New XmlSerializer(GetType(List(Of Session_CSV_file)))
Using writer As New StreamWriter("C:\Users\xxx\Documents\XMLtest.xml")
serializer.Serialize(writer, lst)
End Using
End Sub
I filled my list as follows but you can fill you list any way you choose.
Private lst As New List(Of Session_CSV_file)
Private Sub FillList()
Using cn As New SqlConnection(My.Settings.CoffeeConnection),
cmd As New SqlCommand("Select Top 10 * From Coffees;", cn)
cn.Open()
Dim reader = cmd.ExecuteReader
While reader.Read
Dim sess As New Session_CSV_file(reader.GetInt32(0), reader.GetString(1), reader.GetString(3))
lst.Add(sess)
End While
End Using
End Sub
To recreate the list... (OOps! forgot this)
Private RehydratedList As New List(Of Session_CSV_file)
Private Sub CreateListFromXML()
Dim serial As New XmlSerializer(GetType(List(Of Session_CSV_file)))
Using fs As New FileStream("C:\Users\maryo\Documents\XMLtest.xml", FileMode.Open)
RehydratedList = DirectCast(serial.Deserialize(fs), List(Of Session_CSV_file))
End Using
For Each item In RehydratedList
Debug.Print(item.ToString)
Next
End Sub

VBA Class with Collection of itself

I'm trying to create a class with a Collection in it that will hold other CASN (kind of like a linked list), I'm not sure if my instantiation of the class is correct. But every time I try to run my code below, I get the error
Object variable or With block not set
CODE BEING RUN:
If (Numbers.count > 0) Then
Dim num As CASN
For Each num In Numbers
If (num.DuplicateOf.count > 0) Then 'ERROR HERE
Debug.Print "Added " & num.REF_PO & " to list"
ListBox1.AddItem num.REF_PO
End If
Next num
End If
CLASS - CASN:
Private pWeek As String
Private pVendorName As String
Private pVendorID As String
Private pError_NUM As String
Private pREF_PO As Variant
Private pASN_INV_NUM As Variant
Private pDOC_TYPE As String
Private pERROR_TEXT As String
Private pAddressxl As Range
Private pDuplicateOf As Collection
'''''''''''''''' Instantiation of String, Long, Range etc.
'''''''''''''''' Which I know is working fine
''''''''''''''''''''''
' DuplicateOf Property
''''''''''''''''''''''
Public Property Get DuplicateOf() As Collection
Set DuplicateOf = pDuplicateOf
End Property
Public Property Let DuplicateOf(value As Collection)
Set pDuplicateOf = value
End Property
''''' What I believe may be the cause
Basically what I've done is created two Collections of class CASN and I'm trying to compare the two and see if there are any matching values related to the variable .REF_PO and if there is a match I want to add it to the cthisWeek's collection of class CASN in the DuplicateOf collection of that class.
Hopefully this make sense... I know all my code is working great up to this point of comparing the two CASN Collection's. I've thoroughly tested everything and tried a few different approaches and can't seem to find the solution
EDIT:
I found the error to my first issue but now a new issue has appeared...
This would be a relatively simple fix to your Get method:
Public Property Get DuplicateOf() As Collection
If pDuplicateOf Is Nothing Then Set pDuplicateOf = New Collection
Set DuplicateOf = pDuplicateOf
End Property
EDIT: To address your question - "So when creating a class, do I want to initialize all values to either Nothing or Null? Should I have a Class_Terminate as well?"
The answer would be "it depends" - typically there's no need to set all your class properties to some specific value: most of the non-object ones will already have the default value for their specific variable type. You just have to be aware of the impact of having unset variables - mostly when these are object-types.
Whether you need a Class_Terminate would depend on whether your class instances need to perform any "cleanup" (eg. close any open file handles or DB connections) before they get destroyed.

ByRef Local Variable using Linq

I'm having trouble with selecting only part of a collection and passing it by reference.
So I have a custom class EntityCollection which is , who guessed, a collection of entities. I have to send these entities over HTTPSOAP to a webservice.
Sadly my collection is really big, let's say 10000000 entities, which throws me an HTTP error telling me that my request contains too much data.
The method I am sending it to takes a Reference of the collection so it can further complete the missing information that is autogenerated upon creation of an entity.
My initial solution:
For i As Integer = 0 To ecCreate.Count - 1 Step batchsize
Dim batch As EntityCollection = ecCreate.ToList().GetRange(i, Math.Min(batchsize, ecCreate.Count - i)).ToEntityCollection()
Q.Log.Write(SysEnums.LogLevelEnum.LogInformation, "SYNC KLA", "Creating " & String.Join(", ", batch.Select(Of String)(Function(e) e("nr_inca")).ToArray()))
Client.CreateMultiple(batch)
Next
ecCreate being an EntityCollection.
What I forgot was that using ToList() and ToEntityCollection() (which I wrote) it creates a new instance...
At least ToEntityCollection() does, idk about LINQ's ToList()...
<Extension()>
Public Function ToEntityCollection(ByVal source As IEnumerable(Of Entity)) As EntityCollection
Dim ec As New EntityCollection()
'ec.EntityTypeName = source.FirstOrDefault.EntityTypeName
For Each Entity In source
ec.Add(Entity)
Next
Return ec
End Function
Now, I don't imagine my problem would be solved if I change ByVal to ByRef in ToEntityCollection(), does it?
So how would I actually pass just a part of the collection byref to that function?
Thanks
EDIT after comments:
#Tim Schmelter it is for a nightly sync operation, having multiple selects on the database is more time intensive then storing the full dataset.
#Craig Are you saying that if i just leave it as an IEnumerable it will actually work? After all i call ToArray() in the createmultiple batch anyway so that wouldn't be too much of a problem to leave out...
#NetMage you're right i forgot to put in a key part of the code, here it is:
Public Class EntityCollection
Implements IList(Of Entity)
'...
Public Sub Add(item As Entity) Implements ICollection(Of Entity).Add
If IsNothing(EntityTypeName) Then
EntityTypeName = item.EntityTypeName
End If
If EntityTypeName IsNot Nothing AndAlso item.EntityTypeName IsNot Nothing AndAlso item.EntityTypeName <> EntityTypeName Then
Throw New Exception("EntityCollection can only be of one type!")
End If
Me.intList.Add(item)
End Sub
I Think that also explains the List thing... (BTW vb or c# don't matter i can do both :p)
BUT: You got me thinking properly:
Public Sub CreateMultiple(ByRef EntityCollection As EntityCollection)
'... do stuff to EC
Try
Dim ar = EntityCollection.ToArray()
Binding.CreateMultiple(ar) 'is also byref(webservice code)
EntityCollection.Collection = ar 'reset property, see below
Catch ex As SoapException
Raise(GetCurrentMethod(), ex)
End Try
End Sub
And the evil part( at least i think it is) :
Friend Property Collection As Object
Get
Return Me.intList
End Get
Set(value As Object)
Me.Clear()
For Each e As Object In value
Me.Add(New Entity(e))
Next
End Set
End Property
Now, i would still think this would work, since in my test if i don't use Linq or ToEntityCollection the byref stuff works perfectly fine. It is just when i do the batch thing, then it doesn't... I was guessing it could maybe have to do with me storing it in a local variable?
Thanks already for your time!
Anton
The problem was that i was replacing the references of Entity in my local batch, instead of in my big collection... I solved it by replacing the part of the collection that i sent as a batch with the batch itself, since ToList() and ToEntityCollection both create a new object with the same reference values...
Thanks for putting me in the correct direction guys!

Serializing and Searching Object Collections

I would appreciate if somebody could answer some questions regarding storing and searching object collections please. If you could give a basic example of any suggestions I would be very grateful.
Storing:
I'm writing an application which needs to search Active Directory for computer objects, and I'm storing them in a collection of objects. I want to save the computer objects collection along with additional information which is not stored in Active Directory (e.g. the computer's MAC address) for the next time the application is run. I also want to save a list of OU's.
Here is my object collection so far (It will have more properties). What is the best method of saving the collection? Preferably not using a database or will saving to a file have a drastic performance impact?
Or is there a better way to do what I have below?
Public Class Computer
Public Property Name As String
Public Property FQDN As String
Public Property Path As String
End Class
Public Class ComputerCollection
Inherits Collections.CollectionBase
Public Sub Add(ByVal computer As Computer) 'Adds a new computer object to the collection
List.Add(computer)
End Sub
End Class
Searching:
I have a layout similar to ADUC with a tree view of OU's and a list view displaying computer objects in the selected OU. The following code loops through the computer object collection checking if the path of the computer object matches the selected OU path and then displays them in the list view.
Is this the best method in terms of performance? or is there a faster way?
Private Sub tvOU_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles tvOU.AfterSelect
lvComputers.Items.Clear()
If tvOU.SelectedNode.Name > "" Then
For Each computerObj In computers
If computerObj.Path.ToString.Replace("CN=" + computerObj.Name + ",", "") = tvOU.SelectedNode.Name Then
Dim lvItem As New ListViewItem
lvItem.Text = computerObj.Name
lvComputers.Items.Add(lvItem)
End If
Next
Else
Exit Sub
End If
End Sub
A simple List(Of Computer) is indeed all you need unless there is some other unknown requirement. Saving is very simple using serialization for this type of thing, and would work the same for a List or Collection<T>.
Imports System.Collections.ObjectModel
Public Class ComputersCollection
Inherits Collection(Of Computer)
' NOT the crude thing in the VB NameSpace
' there is almost nothing to add: item, add and remove
' are in the base class
' ... perhaps saving and retrieving them by a key (name)
' which may do away with the search procedure in the posted code
End Class
Private Computers As New Collection(Of Computer)
' or
Private Computers As New List(Of Computer)
The other part, saving your collection, can be simple with serialization. First, you will have to add the Serializable attribute to your Computer class:
<Serializable>
Public Class Computer
If you forget, you get the error Class foobar is not marked as serializable. Save to disk:
Imports System.Runtime.Serialization
Private myComputerFile As String = ...
Dim bf As New BinaryFormatter
Using fs As New FileStream(myComputerFile , FileMode.OpenOrCreate)
bf.Serialize(fs, Computers)
End Using
Then you can recreate the List or Collection just as easily; this time the List:
' ToDo: add a check to skip if file is not there
' as in the first time run
Dim bf As New BinaryFormatter
Using fs As New FileStream(myComputerFile , FileMode.Open)
Computers = CType(bf.Deserialize(fs), List(Of Computers))
End Using
Alternatively you can use the XML serializer to create an XML File or use the faster, smarter binary serializer is ProtoBuf-Net

Reading and writing to an xml file with VB.Net

I am rather new to the programming world ( I'm a network guy ). I have however been asked to develop a front end that configures an xml file for a console application. The console application reads from this xml file and opens multiple instances of a browser, one instance per monitor (6 monitors in total). There are 4 control centers, each of which has 6 monitors. Each control center is run off a seperate pc. These pc's are not on the network and do not have access to each other. I have been told I can also not use a database.
Each monitor will display one website at a time, there could be multiple sites listed to be displayed on the specific monitor so they will periodically change. Each control center will show different sites.
My first question: Is this XML valid?
<ControlCenter>
<Monitor>
<monitor_id>0</monitor_id>
<browser_short_na>ie</browser_short_na>
<url_list>
<url>
<url_id>0</url_id>
<url_na><![CDATA[http://www.hmv.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
<url>
<url_id>1</url_id>
<url_na><![CDATA[http://www.amazon.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
<url>
<url_id>2</url_id>
<url_na><![CDATA[http://www.google.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
</url_list>
</Monitor>
<Monitor>
<monitor_id>1</monitor_id>
<browser_short_na>ie</browser_short_na>
<url_list>
<url>
<url_id>0</url_id>
<url_na><![CDATA[http://www.amazon.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
</url_list>
</Monitor>
</ControlCenter>
What I do so far is open the xml file and add all the monitors to a combobox
Dim dom As New Xml.XmlDocument
dom.Load("test.xml")
ComboBox1.Items.Clear()
Dim monitorid As String = String.Empty
For Each node As Xml.XmlNode In dom.SelectNodes("//ControlCenter/Monitor/monitor_id")
monitorid = node.InnerText
ComboBox1.Items.Add(monitorid)
Next
This is now where I am stuck. Once the users selects one of the monitors from the combobox I then need to get all the information for that monitor. So I need the browser_short_na, and all the urls all based on the monitor_id selected.
I have tried creating a dataset, loading the xmlfile using readxml. I then tried creating a dataview pointing to that dataset. Tried adding a RowFilter to the dataview.
Dim val As String = ComboBox1.SelectedItem.ToString
Dim dsXmlFile As New DataSet
dsXmlFile.ReadXml("test.xml")
Dim dv As New DataView
dv.Table = dsXmlFile.Tables(0)
Dim drv As DataRowView
dv.RowFilter = "monitor_id = " & val
Dim url As String = ""
'Retrieve my values returned in the result
For Each drv In dv
url = drv("url_na")
Next
When I step through the code and look at the for each loop it fails with the message "url_na is neither a DataColumn nor a DataRelation for table Monitor."
I am thinking I am not handling the url_list section correctly.
Once all the information for the selected monitor is read I will display the values in textboxes/listboxes which the users can then edit. If they then save it should write the new values to the xml file. They could also choose to add additional urls to the list, or even create an entirely new monitor section.
Any help/suggestions would be greatly appreciated.
I would go the other way around for your issue:
Define a Class (or several classes) that will hold the data you need.
Use a simple serializer to load/save from/to that file.
If you do so, then your issue with the selection is just a classical WPF issue.
Public Class Monitor
Public Property MonitorId As integer
Public Property ListOfUrl As List(Of String)
End Class
MonitorsConfiguration will then reference a List(Of Monitor) object.
You can use a ViewModel object to handle a MonitorsConfiguration easily.
This ViewModel has a SelectedMonitorIndex property, and updates a UrlForThisMonitor List of url when the index property changes.
(Obviously it should implement INotifyPropertyChanged)
Ok so a little view on what the ViewModel might look like :
Public Class MonitorsConfigurationVM
Implement INotifyPropertyChanged
' creates a new VM. Throws exception if file name is not valid
Public Sub New(ConfigFileName As String)
_FileName = ConfigFileName
_MonitorsConfiguration = // Deserialization of the file //
_MonitorIndex = 0
End Sub
Public Property MonitorIndex As integer
Get
return _MonitorIndex
End Get
Set (Value)
if (_MonitorIndex = value) then return
' you might want to perform check here and allow only valid index
_MonitorIndex = value
_UrlIndex=0
NotifyPropertyChanged("MonitorIndex")
NotifyPropertyChanged("MonitorUrls")
NotifyPropertyChanged("HasUrl")
NotifyPropertyChanged("UrlIndex")
End Set
End Property
Public ReadOnly Property HasUrl As Boolean
Get
return Not (MonitorUrls Is Nothing OrElse MonitorUrls.count = 0 )
' ( might be used to disable the listbox bound to MonitorUrl )
End Get
End Property
Public ReadOnly Property MonitorUrls As List(Of String)
Get
return _MonitorConfiguration(_MonitorIndex).ListOfUrl '(you might want to chk)
End Get
End Property
Public Property UrlIndex As Integer
Get
return _UrlIndex
End Get
Set (value)
if value = _UrlIndex then return
' you might want to perform some check here
_UrlIndex = value
NotifyPropertyChanged("UrlIndex")
End Set
End Property
' And Also : AddMonitor / AddUrl / SaveConfiguration / ...
Private _FileName As String = Nothing
Private _MonitorsConfiguration As List(Of Monitor)=Nothing
Private _MonitorIndex As integer = 0
Protected Sub NotifyPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
You should use XPath for parsing the XML Document.
Load the xml file in XMLDocument
Create a XMLNodeList and use XPath to select nodes from loaded xml
document.
Then parse the nodes list and extract all info for selected monitor
node based on id.
Here are XPath expression helpful to you:
For selecting monitor nodes from file: "/ControlCenter/Monitor"
For selecting browser_na field based on monitor id: "/ControlCenter/Monitor[monitor_id='0']/browser_short_na"
For selection urls based on monitor id: "/ControlCenter/Monitor[monitor_id='0']/url_list/url/url_na"
Hope it works for you.