Custom Classes and Collection Classes - vb.net

I have some custom classes and collection classes for those classes. I'm kind of new to actually implementing all of this, and I see a lot of different options but all with their own pros and cons, and I'm having trouble determining the best/right way to do what I need.
For example, I have a Product class with ID and Description properties. I have a ProductCollection class that I basically want to be a dictionary consisting of Product objects. This is a dictionary because there is a performance gain in accessing by key value, which is how I'll reference the objects. The key is Product.ID, and the value is Product.
I'd like to be able to do something like ProductCollection.Add(Product), and the collection will handle signing the dictionary key from the object's ID property. How do I best accomplish this? Implement Dictionary(Of Integer, Product) and override basically all methods to pass in the object and property separately? Or is there a better way?
Also, to give a little more background information to help clarify usage of these classes, there will be a "master" instance of ProductCollection containing all possible products. A ShipTo class will also have a ProductCollection containing specific products that are applicable for that particular ShipTo. My question was specific to the Product/ProductCollection classes, but also applied to the ShipTo classes.

This is how I would hold a collection of products. I have a Product class and a Products collection class.
First create the Product class which holds all the properties:
Imports System.Collections.ObjectModel
Public Class Product
Public Key As String
Public Sub New(ByVal id As Integer,
ByVal description As String)
_id = id
_description = description
End Sub
Private _id As Integer
Public ReadOnly Property ID() As Integer
Get
Return _id
End Get
End Property
Private _description As String
Public ReadOnly Property Description() As String
Get
Return _description
End Get
End Property
End Class
The create a Products collection class to hold the Product class:
Public Class Products
Inherits KeyedCollection(Of String, Product)
Protected Overrides Function GetKeyForItem(ByVal item As Product) As String
Return item.Key
End Function
End Class
You would then use these like this:
Dim myProducts As New Products
myProducts.Add(New Product(1,"Table"))

Related

Is there anything particular you have to do to make a constructor function?

So to start this off; I'm a beginner in VisualBasic.Net and my classes require me to learn it. The current subject is object constructors and constructor methods. The current exercise (it's not graded or an exam) is requiring us to make a parent class with a constructor method, and a child class with a new() that calls said function. It looks a bit like this;
Protected MustInherit Class Vehicle()
Protected ReadOnly Property Serial_No As Integer
Protected Property Mileage As Integer
Protected Property Color As String
Protected Function CreateVehicle() As Object
End Function
End Class
Public Class Car
Inherits Vehicle
Public ReadOnly Property Car_Type As String
Public Sub New()
End Sub
End Class
The thing I'm having issues with is that I'm not sure how to go about it? Can't ReadOnly properties ONLY be edited in the constructor itself, and doesn't the object need to be initialized in the constructor? Is there something particular I need to add in the CreateVehicle function?
I did ask the teacher but his answer was 'just give up on it and go do something else', which is ultimately pretty unhelpful.
Edit: (added the inheritance to the child class)
So, after being asked for clarification on what I'm trying to do; the exercise itself is not entirely about doing this, but it is the thing in the exercise that I'm struggling with. The goal is to create a Car object utilizing the constructor (New()), but the constructor must call a secondary function located inside the parent class, Vehicle.
My issue is the following : I'm not sure how to go about implementing the function inside the constructor. I know how to call methods/subs/functions and how to get returns from them, but I'm not sure on how I would go about returning a ReadOnly property's values from a secondary function. Don't readonly properties become uneditable outside of the constructor?
I could always return each value separately instead of as an object, and then set the Car object's values to be equal to the return of the function, individually. But then what's the point of calling a separate function instead of just passing everything as a parameter and doing it directly in the constructor?
This is probably what your teacher is looking for:
Public MustInherit Class Vehicle
Protected ReadOnly Property Serial_No As Integer
Protected Sub New(serialNumber As Integer)
Me.Serial_No = serialNumber
End Sub
End Class
Public Class Car
Inherits Vehicle
Public ReadOnly Property Car_Type As String
Public Sub New(serialNumber As Integer, carType As String)
MyBase.New(serialNumber)
Me.Car_Type = carType
End Sub
End Class
Both constructors take in parameters so the ReadOnly properties can be set.

Entity Framework Conceptual Clarification

This question is purely conceptual! The code below works fine, but I can't figure out how . . .
The Scenario
I've begun reading up on Entity Framework concepts, and I'm currently using the information and examples located here to build my first MVC project. Here's the code example from that link:
Imports System.Collections.Generic
Imports System.Data.Entity
Namespace MyDataAccessDemo
Module Program
Sub Main()
Using context As New ProductContext()
Dim food As New Category With {.CategoryId = "FOOD"}
context.Categories.Add(food)
Dim cheese As New Product With {.Name = "Cheese"}
cheese.Category = context.Categories.Find("FOOD")
context.Products.Add(cheese)
context.SaveChanges()
End Using
End Sub
End Module
Public Class ProductContext : Inherits DbContext
Public Property Products As DbSet(Of Product)
Public Property Categories As DbSet(Of Category)
End Class
Public Class Product
Public Property ProductId As Integer
Public Property Name As String
Public Property Category As Category ' <-- Circular reference?
End Class
Public Class Category
Public Property CategoryId As String
Public Property Name As String
Public Property Products As ICollection(Of Product) ' <-- Circular reference?
End Class
End Namespace
The Problem
So, "Category" is a class and "Product" is a class. Category contains a collection of its products, each product of which contains its category, which contains a collection of its products, each product of which contains its category, which contains a collection of products . . . well, you get the idea.
The Question
Why does this work? Shouldn't this cause some kind of circular reference? I would think the category would contain the product IDs and vice versa, not the objects themselves.
Those are called Navigation Properties, you can find out more information here on the Entity framework website.

Circle reference (provider -> model -> provider)

i'm creating a new way to do the things in my project and i need some help in some point.
I have 3 projects in my solution:
Buusiness, Provider and Model.
The MODEL project, is the one that i have the classes just like the tables in database:
Ex:
public class Person
property Id as integert?
property Name as string
property CPF as string
end class
the PROVIDER project, is the one that makes the communication with database:
public class PersonProvider
public function ListPerson(filter as model.person) as list(of model.person)
public sub insertPerson(byRef person as model.peson)
public sub updatePerson(byRef person as model.peson)
public sub deletePerson(byRef person as model.peson)
end class
and finally, i have the BUSINESS project, that's the project that exposes the CRUD methods to the website (the website must not have access to the PROVIDER, because we have business logic in the BUSINESS)
public class PersonBusiness
public function ListPerson(filter as model.person) as list(of model.person)
return (new provider.PersonProvider).listPerson(filter)
end public
public sub InsertPerson(byRef person as model.person)
dim provider as new provider.PersonProvider()
provider.insertPerson(person)
end public
...
end class
But, i have come to a problem when i need some inner joins like this:
table CUSTOMER have an id_person, so in model.Customer i have a property IdPerson as integer?
but i want to have the properties Name and CPF that the model.Person have, read only property in the model.Customer, so i've made:
Property IdPerson As Integer?
Private _person As Pessoa
Private ReadOnly Property Person As model.Person
Get
If IsNothing(_person) Then
_person = New provider.PersonProvider.ListPerson(new model.Person with {.Id = Me.IdPerson})
End If
Return _person
End Get
End Property
ReadOnly Property Name As String
Get
Return Me.Person.Name
End Get
End Property
ReadOnly Property CPF As String
Get
Return Me.Pessoa.CPF
End Get
End Property
and here comes the question... i found that i can't have reference like:
Website reference Model and Business
Provider reference Model
Business reference Model and Provider
but to do the readonly Properties the Model needs reference to Provider, and that causes a circle reference..visual studio doesn't allow me to do this...
any idea how to do this?
Sorry that my post become so long, i just wanted to make things clear.
Instead of accessing Model by Website and Business, you could add another class that references Provider and Person, and this new class will referenced by Website and Business (instead of Website and Business referencing Person directly).

How to add a "sub property" to a class property

If, in code, I wanted to do something like the following, what would my class definition need to look like? (Keep in mind the fruit/language thing is just an example)
dim myfruit as new fruit()
myfruit.name = "apple"
myfruit.name.spanish = "manzana"
Here is the class I have, just not sure how to add the "sub property".
Public Class Fruit
Private _name As String
Public Property name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
In general, for you to have a "sub property", you'd need to make your Property a class itself. This would mean the subproperty is actually a property on the class exposed by the top level property.
Effectively, you'd change the name property from a string to a "Translations" class or similar, ie:
Public Class Fruit
Public Property Name As New Translations
End Class
Public Class Translations
Public Property Primary As String
public Property Spanish As String
End Class
However, this will likely break the code you're displaying, as the second line would need to have a different syntax, ie:
myfruit.Name.Primary = "green"
myfruit.Name.Spanish = "verde"
However, if the goal here is to just handle translation of your user interface, there are other options. For details, see Introduction to International Applications Based on the .NET Framework on MSDN.
I initially thought Reed´s answer was what I was after. In my application, I wanted to use the "sub-property" to set a property on a Form Label. (I was trying to emit only the Label properties I wanted available to a Custom Control.)
I tried this:
Public Class Fruit
Private _name As New Translations
Public Property Name As Translations
Get
Return _name
End Get
Set(value As Translations)
_name = value
_PrimaryCaps = _name.Primary.ToUpper
End Set
End Property
'Private variable is automatically added for unexpanded property
Public Property PrimaryCaps As String
End Class
Public Class Translations
Public Property Primary As String
Public Property Spanish As String
End Class
Then
Dim myFruit As New Fruit
myFruit.Name.Primary = "Apple"
myFruit.Name.Spanish = "Manzana"
Dim primaryCaps As String = myFruit.PrimaryCaps
Weirdly - to me at least - this doesn't work; myFruit.PrimaryCaps returns nothing rather than the hoped-for "APPLE". It appears that the Set for Name is never executed. (Placing the _PrimaryCaps assignment above the Get Return does work, however.)
(I realize that a PrimaryCaps property could be added to the Translations class but, again, this doesn't help if you're wanting to set a foreign variable from within an instance of Fruit.)
I don't know if this is "by-design", whether I've simply misunderstood the intended functionality or what. One thing I did alight on after further research was that this structure isn't very common at all in .NET; for example setting a control's size is done as follows:
oControl.Size = New Drawing.Size(20, 15)
rather than simply setting, say, the Width property directly:
oControl.Size.Width = 20
(The latter won't compile: "Expression is a value and therefore cannot be the target of an assignment.")
If anyone has any more insight than I on this, I'd love to hear it. I know this could simply be done by using an instance of Fruit, for example, but that's not the point.

Weird VB.NET array-property situation

I have this weird situation.
I have these two classes:
Public Class Entry
End Class
Public Class Core
End Class
One of the properties of the Core class will be an array of Entry objects. How can I declare it?
Now, the only way to change (add/remove) this array from outside should be using two functions - AddEntry(Ent As Entry) and RemoveEntry(ID As String). Note that here, whoever is calling the AddEntry function should only be bothered with creating an Entry object and passing it. It will be added to the existing array.
But, the Entry array should be accessible like this from outside, for looping through and printing or whatever like this:
' core1 is a valid Core object
For Each Ent As Entry In core1.Entries
MsgBox(Ent.SomeProperty)
Next Ent
Is it possible to expose the Array as a property but restrict modification through functions alone? I know that the logic inside the Add and Remove functions can be inside the setter or getter, but the person wanting to add should pass only a single Entry object.
It is like saying You have readonly access to the array, but for modifying it, just create an object and send it or the ID to remove it. Don't bother about the entire array.
I hope I am making sense.
Why do you want to expose it as an array ?
What I would do, is use a List internally to store the entries. (That List would be private)
Create the necessary public methods (AddEntry / RemoveEntry / ... ), which manipulate the private list.
Then, create a public property which exposes the List, but in a ReadOnly fashion. That is, that property should return an ReadOnlyCollection instance.
Like this:
(I know it is in C#, but that 's my 'main language' - a bit too lazy to convert it to VB.NET)
public class Core
{
private List<Entry> _entries = new List<Entry>();
public void AddEntry( Entry entry )
{
_entries.Add (entry);
}
public ReadOnlyCollection<Entry> Entries
{
get { return _entries.AsReadOnly(); }
}
}
EDIT: VB.Net version provided by MarkJ
Imports System.Collections.ObjectModel
Public Class Core
Private _entries As New List(Of Entry)
Public Sub AddEntry( new As Entry )
_entries.Add (new)
End Sub
Public ReadOnly Property Entries() As ReadOnlyCollection(Of Entry)
Get
Return _entries.AsReadOnly
End Get
End Property
End Class
Create a private field for the array and then create your accessing methods to work with the array internally. In order to expose this array to callers so that they can enumerate it you should expose a property of type IEnumerable(Of T).
This approach is not foolproof, however as a clever caller could simply cast the IEnumerable(Of T) back to an array and modify it so it may be necessary to create a copy of the array and return that as the IEnumerable(Of T). All this has obvious performance penalties as I am sure you already see. This is just one of many issues that can arise when arrays are used as underlying data structures.
You can keep the List private and instead return an IEnumerable. Code generated via Reflector - I hope it's readable:
Public Class Core
Public Sub AddEntry(ByVal Ent As Entry)
Me.entries.Add(Ent)
End Sub
Public Sub RemoveEntry(ByVal ID As String)
Dim pred As Predicate(Of Entry) = Function (ByVal entry As Entry)
Return (entry.Id = ID)
End Function
Me.entries.RemoveAll(pred)
End Sub
Public ReadOnly Property Entries As IEnumerable(Of Entry)
Get
Return Me.entries
End Get
End Property
Private entries As List(Of Entry) = New List(Of Entry)
End Class
Note: I'd recommend using a List<Entry> instead of an array if you'll be adding and removing objects - or perhaps even a Dictionary<string, Entry> given the way you are using it.