Generic Collections, Member Classes, Design Pattern question for VB.NET - vb.net

I have a class called Person:
Public Class Person
Private PersonID as String
Private Name as String
Private Records as GenericCollection(Of PublicRecord)
Public Sub New(ByVal ID as String)
Me.PersonID = ID
Me.Name = getPersonName(ID)
End Sub
'Get/Sets
End Class
getPersonName is simply a function that does exactly as it is described. GenericCollection class is as follows:
Public Class GenericCollection(Of ItemType)
Inherits CollectionBase
' Purpose: Provides a generic collection class from which all other collections
' classes can be inherited if they wish to extend the functionality below.
#Region "Public Methods"
Public Function Add(ByVal NewObject As ItemType) As Integer
Return MyBase.InnerList.Add(NewObject)
End Function
Public Sub New()
MyBase.New()
End Sub
#End Region
#Region "Public Properties"
Default Public Property Item(ByVal Index As Integer) As ItemType
Get
Return CType(MyBase.InnerList(Index), ItemType)
End Get
Set(ByVal value As ItemType)
MyBase.InnerList(Index) = value
End Set
End Property
#End Region
End Class
PublicRecord class is:
Public Class PublicRecord
Private RecordID As String
Private RecordDataOne As String
Private RecordDataTwo As String
Public Sub New()
MyBase.New()
End Sub
'Get/Sets
End Class
One of the requirements I've been told can be done is that I should be able to grab all Persons in a Collection of Persons, then since all of those Persons will have Collectinos of Records within them... grab a specific set of data from the Collection of Records.
We'll say, I want to: getPersonsOverAge21() from the Collection of Records inside each Person inside the Collection of Persons.
Is this even possible? If so, can someone explain how it would work?

There's no need to implement your own generic collection class. .Net has already done this for you in the System.Collections.Generic namespace. Look at a List(Of Person) or even just a simple IEnumerable(Of Person).
Now you haven't explained how your record objects relate to your person type or what data they contain, so I can only speculate on the next part. But it sounds kind of like you want something like this:
Dim people As List(Of Person) = GetPeopleFromDatabase()
Dim peopleOver21 As IEnumerable(Of Person) = people.Where(Function(p) p.Age >= 21)

Dim peopleOver21Query = From p In people _
Where (p.Age >= 21) _
Select p

Related

Sharing View Model instance in different collections

Are there best practices for creating and sharing a View Model instance between different collection objects? For example, here's a scenario of School and Class classes that both have collections of Student objects:
Public Class Student
Public Property FirstName As String
Public Property LastName As String
Public Property Id As Integer
End Class
Public Class [Class]
Public Property Id As Integer
Public Property Name As String
Public Property Students As New Dictionary(Of Integer, Student)
Public Event StudentAdded(sender As Object, e As StudentEventArgs)
Public Sub AddStudent(student As Student)
If Not Students.ContainsKey(student.Id) Then
Students.Add(student.Id, student)
RaiseEvent StudentAdded(Me, New StudentEventArgs(student))
End If
End Sub
End Class
Public Class School
Public Property Classes As New Dictionary(Of Integer, [Class])
Public Property Students As New Dictionary(Of Integer, Student)
Public Event ClassAdded(sender As Object, e As EventArgs)
Public Event StudentAdded(sender As Object, e As StudentEventArgs)
Public Sub AddClass([class] As [Class])
If Not Classes.ContainsKey([class].Id) Then
Classes.Add([class].Id, [class])
RaiseEvent ClassAdded(Me, EventArgs.Empty)
End If
End Sub
Public Sub AddStudent(student As Student)
If Not Students.ContainsKey(student.Id) Then
Students.Add(student.Id, student)
RaiseEvent StudentAdded(Me, New StudentEventArgs(student))
End If
End Sub
End Class
Public Class StudentEventArgs
Inherits EventArgs
Public Property Student As Student
Public Sub New(student As Student)
Me.Student = student
End Sub
End Class
When creating a View Model for School and Class, both could end up creating View Models for the same Student objects:
Public Class SchoolViewModel
Inherits BaseViewModel
Private WithEvents _school As School
Public Sub New(school As School)
_school = school
End Sub
Public Property Students As New ObservableCollection(Of StudentViewModel)
Public Property Classes As New ObservableCollection(Of ClassViewModel)
Private Sub _school_StudentAdded(sender As Object, e As StudentEventArgs) Handles _school.StudentAdded
' Create a new View Model for the Student
Students.Add(New StudentViewModel(e.Student))
End Sub
End Class
Public Class ClassViewModel
Inherits BaseViewModel
Private WithEvents _class As [Class]
Public Sub New([class] As [Class])
_class = [class]
End Sub
Public Property Students As New ObservableCollection(Of StudentViewModel)
Private Sub _class_StudentAdded(sender As Object, e As StudentEventArgs) Handles _class.StudentAdded
' Create a new View Model or try to get the one created by the SchoolViewModel?
Students.Add(New StudentViewModel(e.Student))
End Sub
End Class
Public Class StudentViewModel
Inherits BaseViewModel
Private _student As Student
Public Sub New(student As Student)
_student = student
With _student
Me.FirstName = .FirstName
Me.LastName = .LastName
Me.Id = .Id
End With
End Sub
Public Property FirstName As String
Public Property LastName As String
Public Property Id As Integer
End Class
My question is regarding the creation of StudentViewModel instances in both SchoolViewModel and ClassViewModel StudentAdded event handlers. Let's assume that the students are first added to the School and then assigned to their classes. The StudentViewModel is pretty simple and creating duplicates might not be a problem but it nonetheless seems wasteful when a View Model for the same student has already been created in the SchoolViewModel.
If I want to share the SchoolViewModel's StudentViewModel, what's a best practices approach for that?
Passing a reference to SchoolViewModel's Students ObservableCollection when creating a new ClassViewModel?
Creating some GetStudentViewModelDelegate function?
Creating a Shared collection of StudentViewModel?
I usually try to limit references between objects and creating new View Models would be a tradeoff for that in this example.
Although I can't help specifically in VB, but you may be best looking into GENERICS.
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/generic-types
It is a way to create more of a top-level class where the internal operations represent a given "type". Ex: Student, School, Subject (instead of a Class type named [Class]. I just see ALL types of confusion downstream.
The article covers some basics, but I think is what you are looking for. You would want the context of things and functions generic too, so no matter what "type" you want, you can nest common functionality.
For example, for each of your student, class, school, you have an "AddStudent", "AddClass", "AddSchool", or similar. If you have a generic with an "Add" method, then it would have the "Add" place-holder / operations once. Then, if you have this ViewModel working as type "Student", and another of "School" that HAS an instance of ViewModel "Student", you could do something like
SchoolObject.StudentObject.Add()
vs
SchoolObject.Add()
The "Of" type can also be declared as a class type that qualifies based on an "Interface" or some parentClassType too. So, if you have a base-class that is common to all Student, Class (Subject), School, then you can generic apply to the INTERFACE. So, please forgive my basic capacities at VB.
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/interfaces/walkthrough-creating-and-implementing-interfaces
Hopefully this may help you in utilizing a concept to better fit your need that you just were not familiar with.

Interface usage within three projects

I will try to simplify as possible. In my solution i got 3 separated projects:
Main - where i use Bal
Bal - business logic, eg. classes:
Artikel
Material
Dal - data layer logic, eg. classes:
DALArtikel
DALMaterial
Now Dal's classes implements interface IDAL which looks like follows:
Public Interface IDAL
Function isExist(name As String) As Boolean
Function GetIdByName(name As String) As Integer
Function GetNameById(pId As Integer) As String
End Interface
Then i can call interface's methods from Bal's project. Every BAL's class has it's DAL class like for Artikel is DALArtikel.
Now every BAL's classes inherits from one Base class which looks like below. This Base class implements interface similar to mentioned above called IGetInformation
Public Class Base
Implements IGetInformation
Property Id As Integer
Property DAL As DataLayer.IDAL
Protected Sub New()
End Sub
Protected Sub New(dal As DataLayer.IDAL)
Me.DAL = dal
End Sub
Protected Sub New(pId As Integer)
_Id = pId
End Sub
Public Overridable Function IsExist(name As String) As Boolean Implements IGetInformation.IsExist
Return DAL.isExist(name)
End Function
Public Overridable Function GetNameById(pId As Integer) As String Implements IGetInformation.GetNameById
Return DAL.GetNameById(pId)
End Function
Public Overridable Function GetIdByName(pName As String) As Integer Implements IGetInformation.GetIdByName
Return DAL.GetIdByName(pName)
End Function
Mentioned interface:
Public Interface IGetInformation
Function isExist(name As String) As Boolean
Function GetIdByName(name As String) As Integer
Function GetNameById(pId As Integer) As String
End Interface
So every Bal's class like Artikel is constructed as following:
Public Class Artikel
Inherits Base
Property Serie As String
Property Nummer As String
Property Name As String
Sub New()
MyBase.New(New DALArtikel)
End Sub
Sub New(pId As Integer)
MyBase.New(New DALArtikel)
Id = pId
End Sub
Sub New(pId As Integer, pSerie As String)
MyBase.New(New DALArtikel)
Id = pId
Serie = pSerie
End Sub
This way i can instatiate artikel class in Main project and call it's e.g isExist method without specyfing specific DAL class associated with it as in Artikel class constructor it was already specified. The problem is now when i want to add new method which will be not in IDAL interface i have to implement like this in Artikel:
Public Function IsExistBarcode(barcode As String) As Boolean
Return New DataLayer.DALArtikel().CheckIfBarcodeExist(barcode)
End Function
so this time i have to specify DALArtikel before i call CheckIfBarcodeExist as my property DAL doesn't know it.
Generally i don't like the way as it is currently, you see that i use two separate exactly the same content interfaces for bal's and dal's projects and the logic behind. Do you know other efficient way which i could change current logic to be let's say 'better'?
Appreciate possible improvment example according to my situation. Sorry for long post but couldn't make it more less. If something unclear let me know.

Need Help Initializing a Generic Property in VB.Net

I've created a request class. Here is an abbreviated version of it:
Public Class Request(Of T)
Private _Account As String
Public Property Account() As String
Get
Return _Account
End Get
Set(ByVal value As String)
_Account = value
End Set
End Property
Private _InnerRequest As T
Public Property InnerRequest() As T
Get
Return Me._InnerRequest
End Get
Set(ByVal value As T)
Me._InnerRequest = value
End Set
End Property
End Class
And then I have two other classes that I intend to use with this one - again, abbreviated
Public Class Individual
Public FirstName As String
Public LastName As String
Friend Sub New()
End Sub
End Class
And
Public Class Commercial
Public EntityName As String
Friend Sub New()
End Sub
End Class
Again, both of these are pretty abbreviated. The issue comes in when I attempt to use the properties of individual or commercial:
Dim Req As New Request(Of Individual)()
Req.InnerRequest.FirstName = "Herman" <-- Null Ref Exception
So... how do I get my inner request null ref exception kicked? I tried simply using Me._InnerRequest = New T in the New sub of Request, but no dice. Is there a way to handle this?
Req.InnerRequest must be set to an object instance of Individual first.
Req.InnerRequest = new Individual()
Req.InnerRequest.FirstName = "Herman"
Or create an instance for InnerRequest with the following modifications
Public Class Request(Of T As {New}) 'Classes of type T must have a public new constructor defined
::
Private _InnerRequest As New T() 'Creates a new class of type T when an instance is created of Request
And make the constructors of the other classes Public instead of Friend.
Than you can directly do
Dim Req As New Request(Of Individual)()
Req.InnerRequest.FirstName = "Herman"
#Barry already answered what the main problem is, but here's an alternate syntax if you prefer object initializers:
Req.InnerRequest = new Individual() With { FirstName = "Herman" }
Or, if you prefer, you could overload the constructor for your Individual class:
Dim individual As New Individual("Herman")
Req.InnerRequest = individual
With the Individual class looking like:
Public Class Individual
Public FirstName As String
Public LastName As String
Friend Sub New()
End Sub
Friend Sub New(firstName As String)
Me.FirstName = firstName
End Sub
End Class
You probably should consider restricting the T to some Entity class:
Public Class Request(Of T As Entity)
From which both Individual and Commercial will inherit:
Public Class Individual : Inherits Entity
Then maybe declare an overridable property Name of type String on this Entity class (which can be abstract/MustInherit), this should provide some flexibility. Otherwise you'd be having a hard time consuming your design pattern.

WCF serializing objects with inheritance

Here's what I am trying to do. I have a WCF restful service, and I need to serialize multiple objects that inherit from the same class.
There is nothing in any of the base classes that needs to be serialized.
Here is a minimal demo that shows what I want to get to work:
<DataContract()>
Public Class BaseObj
<DataMember()>
Public ID As Integer
Public Sub New(ByVal idval As Integer)
ID = idval
End Sub
End Class
<DataContract()>
Public Class TestObj1
Inherits BaseObj
Public Sub New(ByVal id As Integer)
MyBase.New(id)
End Sub
End Class
' Different from TestObj1 in real life
<DataContract()>
Public Class TestObj2
Inherits BaseObj
Public Sub New(ByVal id As Integer)
MyBase.New(id)
End Sub
End Class
And here's the code that uses it:
<ServiceContract()>
<AspNetCompatibilityRequirements(RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerCall)>
Public Class Service1
<WebGet(ResponseFormat:=WebMessageFormat.Json, UriTemplate:="Test?reqReportID={reqReportID}")>
Public Function GetCollection(ByVal reqReportID As Integer) As List(Of BaseObj)
Dim myObjs As New List(Of BaseObj)
myObjs.Add(New TestObj1(20))
myObjs.Add(New TestObj2(20))
Return myObjs
End Function
End Class
If I declare the List to be a list of TestObj1 instead, everything works.
Am I missing some crucial concept here?
EDIT:
The problem gains a new level of confusion by looking at this code:
<WebGet(ResponseFormat:=WebMessageFormat.Json, UriTemplate:="Test?reqReportID={reqReportID}")>
Public Function GetCollection(ByVal reqReportID As Integer) As BaseObj()
Dim myObjs As New List(Of BaseObj)
myObjs.Add(New TestObj1(20))
myObjs.Add(New TestObj2(20))
' This guy works. Yields correct result of [{"ID":20},{"ID":20}] )
Dim testStr As String = New JavaScriptSerializer().Serialize(myObjs.ToArray())
' But this guy still has problems...
Return myObjs.ToArray()
End Function
What you are missing is a [KnownType] attribute.
WCF requires a way of knowing all possible types so that it could publish the WSDL.
Have a look here.
UPDATE
The problem is that List<T> is not covariant.
Use IEnumerable<T> instead.

Generic Collections

In VB6, there used to be a Collection data type that would allow retrieval of an item in the collection by either its key or its ordinal. However, it wasn't strongly typed.
Now, with VB.Net, I am looking for a suitable replacement that is strongly type and could be used with a generic collection.
This is a simple example of what I want to do. The only problem is that the underlying collection class, BindingList, does not support efficient retrieval of an item by an alpha key, so I have to loop through the elements to get the item I am looking for. For large collections, this is not efficient.
I have looked though the various Collection type classes and have found no suitable replacement.
This is what I want to do, except for the looping that is done with the Item property.
Rather than just saying "Use Hash tables" or something like that, if you could, please include the detailed out as I have done for the short example below.
Public Class Car
Public Sub New(ByVal keyName As String, ByVal property1 As String)
_KeyName = keyName
_Property1 = property1
End Sub
Dim _KeyName As String
Public Property KeyName() As String
Get
Return _KeyName
End Get
Set(ByVal value As String)
_KeyName = value
End Set
End Property
Public _Property1 As String
Public Property Property1() As String
Get
Return _Property1
End Get
Set(ByVal value As String)
_Property1 = value
End Set
End Property
End Class
Public Class Cars
Inherits System.ComponentModel.BindingList(Of Car)
Public Overloads ReadOnly Property Item(ByVal key As String) As Car
Get
For Each CurrentCar As Car In Me.Items
If CurrentCar.KeyName = key Then
Return CurrentCar
End If
Next
Return Nothing
End Get
End Property
End Class
I believe you're looking for Dictionary<TKey, TValue>. In fact, if you do want your own collection class that's strongly typed and isn't (itself) generic, if you change your parent class to Dictionary<string, Car>, you should be all set. This all does, of course, assume that you add the cars to the collection with an explicit string key. If you want the lookup to be based on the value of a property in the collection, you'd do better either using or inheriting from List<Car> and using LINQ to query the list. You could then have...
Public Overloads ReadOnly Property Item(ByVal key As String) As Car
Get
Return (from c in Me where c.KeyName = key select c).SingleOrDefault()
End Get
End Property
Do you really need both access by key AND index?
If you do not, then use a Dictionary(Of String, Car), and use
- MyCol.Items("XXX") to retrieve an item by key (or the shorthand MyCol("XXX"))
- MyCol.ContainsKey("XXX") to test if a key exists in the collection
- For Each Entry as KeyValuePair(Of String, Car) in MyCol if you want to enumerate all objects AND their key
- For Each Entry as Car in MyCol.Values if you want to enumerate the entries without consideration for the key
If you need both access by index and key, I'm afraid your best bet is to use a List(of Car) and a Dictionary(of Car) rolled into one custom collection class, because I believe they went away from that kind of collection which is not really all that useful for most problems.
This is what I am thinking is my best solution. I welcome comments for a better way!
Imports Microsoft.VisualBasic
Public Class Car
Implements Xs(Of Car).IKeyName
Private _KeyName As String
Public Sub New(ByVal keyName As String, ByVal property1 As String)
_KeyName = keyName
_Property1 = property1
End Sub
Public Property KeyName() As String Implements Xs(Of Car).IKeyName.KeyName
Get
Return _KeyName
End Get
Set(ByVal value As String)
_KeyName = value
End Set
End Property
Public _Property1 As String
Public Property Property1() As String
Get
Return _Property1
End Get
Set(ByVal value As String)
_Property1 = value
End Set
End Property
End Class
Public Class Cars
Inherits System.ComponentModel.BindingList(Of Car)
Public Overloads ReadOnly Property Item(ByVal key As String) As Car
Get
For Each CurrentCar As Car In Me.Items
If CurrentCar.KeyName = key Then
Return CurrentCar
End If
Next
Return Nothing
End Get
End Property
End Class
Public Class X
Private _KeyName As String
Public Property KeyName() As String
Get
Return _Keyname
End Get
Set(ByVal value As String)
_Keyname = value
End Set
End Property
End Class
Public Class Xs(Of X)
Inherits Hashtable
Interface IKeyName
Property KeyName() As String
End Interface
Public Shadows Sub Add(ByVal item As IKeyName)
MyBase.Add(item.KeyName, item)
End Sub
Public Shadows ReadOnly Property Item(ByVal key As String) As x
Get
If Me.ContainsKey(key) Then
Return MyBase.Item(key)
Else
'If I mispell a key, I don't want to end up creating a new mispelled version, I want an error
Throw New Exception("Element with key " & key & " is not found")
End If
End Get
End Property
End Class
Public Class Cars2
Inherits Xs(Of Car)
End Class
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim MyCar As New Car("key1", "prop1")
'First approach
Dim MyCars As New Cars
MyCars.Add(MyCar)
Dim RetrievedCar As Car = MyCars.Item("key1") 'Inefficient retrieval by key (uses looping)
'Second approach
Dim Cars2 As New Cars2
Cars2.Add(MyCar)
Dim RetrievedCar2 As Car = Cars2.Item("key1") 'Can now efficiently retreive an item by its key
End Sub
The OrderedDictionary in the System.Collections.Specialized namespace can be accessed by index and by key, if you ever need that. But looking at your solution, it looks like a standard Dictionary, but less efficient because it forces a string type for keys.
Is there any reason you can't use the Dictionary .NET provides you, or another collection type that's already in .NET like OrderedDictionary?