DNN Dal+ - retrieve individual info class collection items (vb.NET) - vb.net

I can't seem to find any answers that work. Here's the setup:
Info class:
Public Class ProductStageInfo
Private _ProductNumber As String
Private _ProductReference As String
Public Sub New()
End Sub
Public Property ProductNumber() As String
Get
Return _ProductNumber
End Get
Set(ByVal Value As String)
_ProductNumber = Value
End Set
End Property
End Class
and so on; I have four class declarations in the info class, the one above has fifteen different items - product number, product reference, product name, and so forth. The other's are catalogue classifications, which 'stage' of production the product is in, quality assurance questions; etc.
Then in the Controller class for DNN, I have those various info classes filled via queries to the DB DNN was deployed on; example:
Public Shared Function LoadStages(ByVal ProductNumber As String) As List(Of ProductStageInfo)
Return CBO.FillCollection(Of ProductStageInfo)(CType(DataProvider.Instance().ExecuteReader("Product_LoadStages", ProductNumber), IDataReader))
End Function
and everything works so far, I can fill a datalist using <%# DataBinder.Eval(Container.DataItem, "ProductNumber" %> and in code behind:
Dim ProductStageList As List(Of ProductStageInfo)
ProductStageList = ProductController.LoadStages(ProductNumber)
ProductStageDataList.DataSource = ProductStageList
ProductStageDataList.DataBind()
so far, so good...
but now I need to allow individuals to 'create' stages, and one of the business reqs' is that people shouldn't be able to create, for example, a delivery stage before a packaging stage.
So, how do I go about 'finding' a product number, product reference, stage number, within a collection? I thought I could fill the collection with all the stages of a certain product number, and then do an if/then stage = 0 found, stage > 5 found, etc.
If ProductStageList.Contains(strProductNumber) then
end if
gives error value of type string cannot be converted to namespace.ProductStageInfo; same thing for ProductStageList.Find...
maybe I just don't understand the whole collection/index/thing. All the examples I've found are regarding single dimension collections - 'how to find name within this collection', and the responses use strings to search through them, but somehow the Info class is being treated differently, and I'm not sure how to translate this...
any hints, tips, advice, tutorials.... appreciate it :)
thanks!

Pretty sure I just found the answer by reviewing another module; basically I need to create an empty object instead of a list object of the same class and use the two to iterate through using for/each, etc.
Dim objStages As ProductStagesInfo
Dim intStages, StageSelected As Integer
Dim intStageOption As Integer = -1
Dim blnValid As Boolean = True
Dim ProductChosen As String = lblStagesCNHeader.Text
Dim ProductStageList As List(Of ProductStagesInfo) = ProductController.LoadStages(ProductChosenNumber)
For intStages = 0 To StageList.Count - 1
objStages = StageList(intStages)
intStageOption += 1
Select objStages.StageSetNumber
Case "0"
Next
objStages._ provides me the ability to get the data I needed to do the business logic
<.<
seems so simple once you see it, wish I could just store it all in my brain
blah!

Related

list.add for a list (of ClassType) not adding to list - no errors reported

;tldr ...Orders.Add(order class variable) does not add a record the the Orders list, but also does not generate any errors.
I'll try to make a long story short and I apologize for the book you are about to read - I'm coming back to VB after spending a while in Python and C#. There is a bit of an explanation before any of the code...
I have a command-line exe program that at its base is working fine. We get 4 differently formatted CSV files that the program reads, and based on the file name, runs them through a parser that pulls the text information out of the file and populates a Public Class that in turn calls a function to populate a database table. The class is a single instance class named DataRecord. This program works absolutely fine (and has for well over 18 months now). It reads one line, if a batch# doesn't exist it creates one, and post the data class to the DB, and loops thru the file one line at a time doing so.
Also, their are 2 files to this program - the OrderAutomation main program file and the DBAccess file (which handle the DataRecord Class and all the various DB calls to verify information and to push the record into the database tables.
It used to be for all 4 file types, each file was a single batch all from a single region (data delimiters) - but now 1 of the file types will start sending multiple regions in a single file (and not necessarily in order) - so I need to modify the program that if its this 1 file type, it goes through the entire file, grouping each region together (each region will get its own Batch#).
I figured this would be fairly simple. I'd create an additional class (VTLData) that has some basic region separation data, and then a list of the DataRecord class. That looks like this:
Public Class VTLData
Public Property Locality As String
Public Property ARProvider As String
Public Property CORegion As String
Public Property BatchID As String
Public Property ProviderZip As String
Public Property IsValidRegion As Boolean
Public Property Orders As List(Of RecordData)
Public Sub New(s As String)
Locality = s
ARProvider = "00000000"
CORegion = "XX"
BatchID = "VXX-00000"
ProviderZip = "00000"
IsValidRegion = True
Orders = New List(Of RecordData)
End Sub
End Class
Like I said earlier - the RecordData class has no changes and is working fine.
There is a point in the program where I know this is the new file type (basically the region information has gone from a zip code to a set of specific text values) so when I test for that, if it's new, I set a boolean NewVTL to true. Because the file can contain more than one region, I have a VTLList property that is a List (of VTLData). I make sure I don't already have a Region in this list already - and if so, reference that index in the list.
When I get to the point where the program normally processes the record, I added the following (VTLIndex is the index variable for the list - at this time its value is 0):
If NewVTL Then
DB.data.CanProcess = CanProcessLine
DB.VTLList(VTLIndex).Orders.Add(DB.data)
Else
. - the old processing method
.
.
End If
I have a watch set for the VTLList(VTLIndex).Orders.Count value - that is at 0 when that line is reached, and stays at 0 when that line is processed.
I've tried creating a Push procedure in the VTLData class where you pass in the db.data class values, create a new instance of the record data and try to assign it that way
Public Sub PushOrder(item As RecordData)
Dim order As New RecordData
With order
.ARProviderNumber = ARProvider
.OrderID = item.OrderID
.AcctSuffix = item.AcctSuffix
.BatchID = BatchID
. - the 70 some other items in the recorddata class
.
.
End With
Orders.Add(order)
End Sub
This would also run without error, but not increase the count of .Orders.Count value.
I'm assuming its something stupid that I'm missing and I'm hoping someone can filter through all this and be like "You forgot to do blah..."
The only other thing I can think of is the do that Push function, but start it with Orders.Add(new RecordData) - and then modify the individual values through an index (.Orders(0).ARProviderNumber = data.ARProviderNumber... etc)
But based on other things I've done in VB - this should be working - I'm sure I'm just missing something...
So thanks for reaching this point - and thank you for letting me know what mistake I've made!!!
So the only way I got this to work for me was to create a Function in the VTLData class that did the following:
Public Function PushOrder(item As RecordData) As Boolean
Dim i As Integer = Orders.Count
Orders.Add(New RecordData)
With Orders(i)
.ARProviderNumber = ARProvider
.OrderID = item.OrderID
.AcctSuffix = item.AcctSuffix
.
. - more fields being populated
.
End With
If Orders.Count <= i Then Return False
Return True
End Function

vb.net use linq to sort collection of structures by two fields

I have a large collection (Me.OmniColl) of the same structure. Two properties of the structure are .partNumber and .rev. I want to sort first by the part number and then by the rev.
I have tried the following:
Dim q = From c In Me.OmniColl Order By c.partNumber Select c
Dim r = From c In q Order By c.rev Select c
Neither q nor r return any results, even though Me.OmniColl contains thousands of entries. I am already using linq to filter the collection successfully, but all of my sorting attempts have failed. If anyone has a solution, please show your code in vb.net. Thanks in advance for your help.
[Edit]
The structure:
<Serializable()> Structure Part
Public Workbook As String
Public Worksheet As String
Public Product As String
Public partNumber As String
Public itemNo As String
Public rev As String
Public partDescription As String
Public unitOfMeasure As String
Public partType As String
Public purchasingCat As String
Public Quantity As Double
Public TotalPerProduct As Double
Public hierarchy As String
End Structure
[Edit]
I want to find a solution without changing from a Collection to some other type and without changing from a Structure to a Public Class. I have a data caching system in place where I serialize the collection of structures and save this in a textfile before closing the program. I then de-serialize this textfile on program opening and edit the collection as needed based on whether changes to the files the collection is generated from have been made. Sorry for making so many edits.
I would create a Generic.List(Of Part) in my opinion. In my example below col is a Generic.List(Of Part) defined at the top of my class, replace it with your's. Mine looked like this:
Dim col As New Generic.List(Of Part)
To get your List ordered...
Dim newCol As Generic.List(Of Part) = (From c In col Order By c.partNumber, c.rev).ToList()
This will order your item's as needed. Then you asked about removing specific items.
You can use the ForEach function which perform's specific action on each element in the list
newCol.ForEach(Sub(x)
If x.Workbook = condition Then
newCol.Remove(x)
End If
End Sub)
Note: This has been tried and tested
Edit:
If you want to get your Collection into a List...
Dim newColl As List(Of Part) = YOURCOLLECTION.Cast(Of Part).ToList

How to get specific index of a Dictionary?

I'm working on a code that returns a query result like MySqlCommand, all working well but what I'm trying to do is insert the result inside a ComboBox. The way for achieve this is the following:
Form load event execute the GetAvailableCategories function
The function executed download all the values and insert it into a dictionary
Now the dictionary returned need an iteration for each Items to insert in the ComboBox
Practice example:
1,3. Event that fire the function
Private Sub Service_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each categoria In Categories.GetAvailableCategories()
service_category.Items.Add(categoria)
Next
End Sub
GetAvailableCategories function
Dim dic As New Dictionary(Of Integer, String)
For Each row In table.Rows
dic.Add(row(0), row(1))
Next
Return dic
How you can see in the 1,3 points I call the function that return the result. What I want to do is insert the row(0) as value of the item and row(1) as Item name. But Actually I get this result in the ComboBox:
[1, Hair cut]
and also I can't access to a specific position of the current item in the iteration. Maybe the dictionary isn't a good choice for this operation?
Sorry if the question could be stupid, but it's a long time since I don't program in vb.net and now I need to brush up a bit.
UPDATE
I've understood that I can assign the value access to the .key of my dictionary, so the result that I want achieve is correct if I do:
cateogoria.key (return the id of record taken from the db)
categoria.value (is the item name that'll display in the ComboBox)
now the problem's that: How to assign the value of the current item without create any other new class? For example:
service_category.Items.Add(categoria.key, categoria.value)
But I can't do this, any idea?
A List as a DataSource sounds like what you are really after. Relying on relative indices in different arrays is sort of flaky. There is not a lot about what these are, but a class would keep the related info together:
Public Class Service
Public Property Name As String
Public Property Category As String
Public Property Id As Int32
End Class
This will keep the different bits of information together. Use them to store the info read from the db and use a List to store all of them:\
Private Services As New List(of Service)
...
For Each row In table.Rows
Dim s As New Service
s.Name = row(0).ToString() '???
s.Category =...
s.Id = ...
Services.Add(s) ' add this item to list
Next
Finally, bind the List to the CBO:
myCbo.DataSource = Services
myCbo.DisplayMember = "Name" ' what to show in cbo
myCbo.ValueMember = "Id" ' what to use for SelectedValue
I dont really know what you want to show or what the db fields read are, so I am guessing. But the larger point is that a Class will keep the different bits of info together better than an array. The List can be the DataSource so that you dont even have to populate the CBO directly. The List can also be Sorted, searched, Filtered and so forth with linq.
When the user picks something, myCbo.SelectedItem should be that item (though it will need to be cast), or you can use SelectedIndex to find it in the list:
thisOne = Services(myCbo.SelectedIndex)
It is also usually a good idea to override ToString in the item/service class. This will determine what shows when a DisplayMember mapping is not available. Without this, WindowsApp2.Service might show for your items:
Public Overrides ToString() As String
Return String.Format("{0} ({1})", Name, Price)
End Sub
This would show something like
Haircut ($12.30)

Sorting a SortedDictionary by key length in Visual Basic?

I'm writing a script that anonymizes participant data from a file.
Basically, I have:
A folder of plaintext participant data (sometimes CSV, sometimes XML, sometimes TXT)
A file of known usernames and accompanying anonymous IDs (e.g. jsmith1 as a known username, User123 as an anonymous ID)
I want to replace every instance of the known username with the corresponding anonymous ID.
Generally speaking, what I have works just fine -- it loads in the usernames and anonymous IDs into a dictionary and one by one runs a find-and-replace on the document text for each.
However, this script also strips out names, and it runs into some difficulty when it encounters names contained in other names. So, for example, I have two pairs:
John,User123
Johnny,User456
Now, when I run the find-and-replace, it may first encounter John, and as a result it replaces Johnny with User123ny, and then doesn't trigger Johnny.
The simplest solution I can think of is just to run the find-and-replace from longest key to shortest. To do that, it looks like I need a SortedDictionary.
However, I can't seem to convince Visual Basic to take my custom Comparer for this. How do you specify this? What I have is:
Sub Main()
Dim nameDict As New SortedDictionary(Of String, String)(AddressOf SortKeyByLength)
End Sub
Public Function SortKeyByLength(key1 As String, key2 As String) As Integer
If key1.Length > key2.Length Then
Return 1
ElseIf key1.Length < key2.Length Then
Return -1
Else
Return 0
End If
End Function
(The full details above are in case anyone has any better ideas for how to resolve this problem in general.)
I think it takes a class that implements the IComparer interface, so you'd want something like:
Public Class ByLengthComparer
Implements IComparer(Of String)
Public Function Compare(key1 As String, key2 As String) As Integer Implements IComparer(Of String).Compare
If key1.Length > key2.Length Then
Return 1
ElseIf key1.Length < key2.Length Then
Return -1
Else
'[edit: in response to comments below]
'Return 0
Return key1.Compare(key2)
End If
End Function
End Class
Then, inside your main method, you'd call it like this:
Dim nameDict As New SortedDictionary(Of String, String)(New ByLengthComparer())
You might want to take a look (or a relook) at the documentation for the SortedDictionary constructor, and how to make a class that implements IComparer.

How to Save/Reload data in vb.net after .exe close?

I am new to vb.net, and this is my first project where I'm fairly certain there is an obvious answer that I just can't find.
Problem: I have a list of a structure I have defined with many properties. I want to be able to edit and load that list with the values I have saved to it before hand after closing the program and loading it backup. What is the best way to do this?
This isn't a simple string or bool, otherwise I would use the user settings that is commonly suggested, in the project's properties. I've seen others that save it into an xml and take it back up, but I'm not inclined to do so since this is going to be distributed to others in mass. Since it's a complex structure, what's the commonly held preferred method?
Example
Here's a structure:
Structure animal
Dim coloring as string
Dim vaccinesUpToDate as Boolean
Dim species as string
Dim age as integer
End structure
And there's a List(Of animal) that the user will add say 1 cat, 2 dogs, etc. I want it so that once the programs is closed after the user has added these, that structure will be saved to still have that 1 cat and 2 dogs with those settings so I can display them again. What's the best way to save the data in my program?
Thanks!
Consider serialization. For this, a class is more in order than an old fashioned Struct:
<Serializable>
Class Animal
Public Property Name As String
Public Property Coloring As String
Public Property VaccinesUpToDate As Boolean
Public Property Species As String
Public Property DateOfBirth As DateTime
Public ReadOnly Property Age As Integer
Get
If DateOfBirth <> DateTime.MinValue Then
Return (DateTime.Now.Year - DateOfBirth.Year)
Else
Return 0 ' unknown
End If
End Get
End Property
' many serializers require a simple CTor
Public Sub New()
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0} ({1}, {2})", Name, Species, Age)
End Function
End Class
The ToString() override can be important. It is what will display if you add Animal objects to a ListBox e.g.: "Stripe (Gremlin, 27)"
Friend animalList As New List(of Animal) ' a place to store animals
' create an animal
a = New Animal
a.Coloring = "Orange"
a.Species = "Feline" ' should be an Enum maybe
a.Name = "Ziggy"
a.BirthDate = #2/11/2010#
animalList.Add(a)
' animalList(0) is now the Ziggy record. add as many as you like.
In more complex apps, you might write an Animals collection class. In that case, the List might be internal and the collection could save/load the list.
Friend Sub SaveData(fileName as String)
Using fs As New System.IO.FileStream(fileName,
IO.FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter
bf.Serialize(fs, animalList)
End Using
End Sub
Friend Function LoadData(fileName as String) As List(Of Animal)
Dim a As List(of Animal)
Using fs As New FileStream(fileName, FileMode.Open, FileAccess.Read)
Dim bf As New BinaryFormatter
a = CType(bf.Deserialize(fs), List(Of Animal))
End Using
Return a
End Function
XMLSerialization, ProtoBuf and even json are much the same syntax. For a small amount of data, a serialized list is an easy alternative to a database (and have many, many other uses, like a better Settings approach).
Calculated Fields as Properties
Notice that I added a BirthDate property and changed Age to calculate the result. You should not save anything which can be easily calculated: in order to update the Age (or VaccinesUpToDate) you'd have to 'visit' each record, perform a calculation then save the result - which might be wrong in 24 hours.
The reason for exposing Age as a Property (rather than a function) is for data binding. It is very common to use a List<T> as the DataSource:
animalsDGV.DataSource = myAnimals
The result will be a row for each animal with each Property as a column. Fields as in the original Structure won't show up. Nor would an Age() function display, wrapping the result as a readonly property displays it. In a PropertyGrid, it will show disabled because it is RO.
Class versus Structure
So if a Structure using Properties will work, why use a Class instead? From Choosing Between Class and Struct on MSDN, avoid using a Structure unless the type meets all of the following:
It logically represents a single value, similar to primitive types (int, double, etc.)
It has an instance size under 16 bytes
It is immutable
It will not have to be boxed frequently
Animal fails the first 3 points (while it is a local item it is not a value for #1). It may also fail the last depending on how it is used.