vb.net class upcasting / downcasting - vb.net

I'm having an issue with casting classes in vb.net, I'm using Visual Studio 2008 with the Compact Framework 3.5 as I'm working on legacy Windows Mobile project.
I have a DLL which acts as the Data Layer for accessing Database objects in SqlCe and I cant change any of the code in there, however I want to add extra functionality to the exposed classes for the Business logic so I created my own classes and Inherited the classes from the Data Layer
Public Partial Class Customers
Public Function ListAll() As IEnumerable(Of Customers)
'Do Work
End Function
End Class
Public Class MyCustomers
Inherits Customers
Public Function FindCustomer(ID As Integer)
End Function
End Class
so in my code I would write something like
For Each c As MyCustomer In Customers.ListAll
'I want to be able to use my c.FindCustomer(), but I get an InvalidCast Exception above.
Next
I get that this is an issue with upcasting / downcasting (I don't remember which way is which), but how can I solve it ?
I can't change the return type of Customers.ListAll() but I need to be able to add methods and properties to implement the business logic.

Inside the For Each loop:
For a one-shot:
DirectCast(c, MyCustomer).FindCustomer(1) 'for id 1
To use more than one time:
Dim customer as MyCustomer = DirectCast(c, MyCustomer)
customer.FindCustomer(1)
You can also do:
With DirectCast(c, MyCustomer)
.FindCustomer(1)
.AnotherMethod()
'etc
End With
Have fun!
Here's an alternative. I'm not sure of the exact architecture of your project, so I'll assume it's like this:
Customers -has a list of Customer
MyCustomers -child of Customers with a list of MyCustomer and more functionalities
Customer -base entry
MyCustomer -base entry with more functionalities
The problem being that you cannot cast an object into it's child (this sort of operation can only work in the other direction), this is basically an impossible problem. Yet you can bypass it with some cloning. This tells me that the base data is the same for Customer and MyCustomer, you only added more methods. Which is great, because it also means that you can manually transform a Customer into a MyCustomer. You just need it to happen automatically.
In the MyCustomers and MyCustomer class, you can add theses:
'In MyCustomers
Public Shared Function GetMyCustomersFromCustomers(customers As Customers) As MyCustomers
Dim data As New MyCustomers()
'copy each modal variable
'create a list of MyCustomer from existing Customer list
For Each c As Customer In customers.listOfCustomers
data.listOfMyCustomers.Add(MyCustomer.GetMyCustomerFromCustomer(c))
Next
Return data
End Function
'In MyCustomer
Public Shared Function GetMyCustomerFromCustomer(customer As Customer) As MyCustomer
Dim data As New MyCustomer
'copy all the data
Return data
End Function
Then if you want to work with your own objects you can extrapolate them from the ones of the dll:
'let's say you currently have a 'customers' as Customers object
Dim myStuff as MyCustomers = MyCustomers.GetMyCustomersFromCustomers(customers)
If you often need the MyCustomers list and don't care about the rest of the class, you can create a Shared function which gives you only the extrapolated list of MyCustomer, no problem.
This works only as long as you can extrapolate MyCustomer from Customers and MyCustomer from Customer, of course.
Hope it helps.

Related

What is the difference between "Class program" and "module program" in VB.net [duplicate]

This question already has answers here:
Classes vs. Modules in VB.NET
(8 answers)
Closed 3 years ago.
I want convert a C# code to vb.net console.
This is first time I find this two type code structure.
1.
Namespace ConsoleApp4
Module Program
Public Sub Main(ByVal args As String())
test()
End Sub
sub test()
end sub
End Module
End Namespace
2.
Namespace ConsoleApp4
Class Program
Public Shared Sub Main(ByVal args As String())
test()
End Sub
shared sub test()
end sub
End Class
End Namespace
what is the difference of this two type?
Sub Main must be shared to work as entry point of the application. It is automatically shared (or static) in modules. In classes the Shared keyword is required.
A VB module corresponds to a C# static class. Static classes and modules have only static members that can be used without having to create an object. In contrast, a non-static class must be instantiated to access its non-static (C#) or non-shared (VB) members
Module M
Public Function F(ByVal x As integer) As Integer
Return x * x
End Function
End Module
Class C
Public Function T(ByVal x As Integer) AS Integer
Return x + 10
End Function
End Class
With these declarations, you can write
Dim r1 As Integer = M.F(5) ' Or simply F(5) '
Dim o As C = New C() ' Must instantiate class, i.e., create an object.'
Dim r2 As Integer = o.T(32)
If you have variables (or properties) in a module, those exist exactly once. You can, however, create many objects from the same class and each object will contain another copy of these variables
Public Class Person
Public Property FirstName As String
Public Property LastName As String
End Class
Using this class declaration you can write
Dim list As New List(Of Person)()
list.Add( New Person With { .FirstName = "Terry", .LastName = "Adams"} )
list.Add( New Person With { .FirstName = "Lisa", .LastName = "Jones"} )
For Each p As Person In list
Console.WriteLine($"Person = {p.FirstName} {p.LastName}")
Next
Now you have two Person objects in the list having different first and last names.
Classes belong to Object-oriented programming (OOP). I suggest you to read some introductions about it, as .NET is mainly based on OOP concepts.
See also:
Object-oriented programming (Visual Basic)
Explain Object Oriented Programming in VB.NET
The difference between a Module and a Class is subtle; there is only ever one instance of a Module in all your program's life, and you cannot make multiple instances of it using the New keyword. By contrast you have to make an instance of a Class before you can use it..
In order to run, the .net framework runtime has to be able to find an available Main method without having to create an instance of a Class. This is achieved either by having the Main be kept inside a Module (and thus the Main is available because the module is available without having to instantiate a Class) or having it declared as Shared and be inside a Class (in which case you can conceive that a special thing happens that makes the shared Subs available without a class instance)
It's a hard difference to explain if you're not really well introduced to the concepts of OO programming and what "instances" actually means:
Class Person
Public Name as String
Public Sub New(n as String)
Name = n
End Sub
End Class
This declares a class of type person. Nothing about it refers to a particular person and it doesn't cause any Person object to exist in the computer memory until you use New:
Dim cj as New Person("Caius Jard")
Dim g as New Person("gxmgxm")
g.Name = "gsmgxm" 'correct a typo! this edits the name in the g object. it leaves cj's name alone
Now two instances of a Person object are in your computer memory, one named for me and one for you. You cannot do this with a Module. If we'd declared Person as Module there would only be one of them in all the entire program, accessed by the reference "Person":
Person.Name = "Caius Jard"
Person.Name = "gsmgxm" 'this overwrites my name. The program cannot remember more than one Person
and we couldn't have multiples. Consider that, at the time you launch your program, the runtime finds everything that is declared to be a Module and creates just one of them. This is somewhat vital for all sorts of advanced reasons that I won't get into, and Modules definitely do have their place in the grand scheme of things but thy aren't always incredibly useful in OO programming because more of the time we want more than one instance of a thing so we can model multiple things simultaneously.
So that's a precis on Class vs Module and why they are. In order to call any Sub or Function, you have to be able to call it on something. You have to have a DVD player before you can put a DVD in it and press Play - equally in programming, you have to have something that you can put your Main sub on, so you (or the .net runtime) can refer to it with Program.Main() and execute the instructions of the Sub.. That's how Subs and Functions work - theyre either associated with the special single instance (if it's a Module or a Shared Sub/Function of a Class), or they're associated with some object instance in the computer memory, and calling the Sub/Function acts on the object instance that was referred to:
Class Person
Public Name as String
Public Sub SetNameBlank()
Name = ""
End Sub
End Class
cj.SetNameBlank() 'the name of the cj object we declared before, is now blank
g.SetNameBlank()
By specifying the object instance name cj then the Sub name, we establish context - the actions listed in SetNameBlank will be carried out against the cj object, not the g one. If we'd declared SetNameBlank as Shared then they would have taken place in the shared context, not related to either cj or g, and Shared SetNameBlank() could be invoked without cj or g even existing
Going back to the reason you're asking your question right now, what the difference between these two (in the context of a "Main" Sub), the answer is..
..is "not much" from the perspective of getting your app going. Either approach is fine. Your Main method will have to start kicking things off my making object instances of the other Classes you have in your program and getting them to do things. It probably wont make new instances of the class that your Main is in so right now it doesn't really matter whether you put your main in a module or a class - it achieves the same end result that there is a Sub the runtime can call into to start things moving, without needing to create an instance of a Class first
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/program-structure/main-procedure

Vb.net 2008 with SQL Server 2005 database

I need some serious help with vb.net system. I'm developing a desktop ordering system, my process flow goes like Customer--->Order------->Invoice------>Payment, NB* all this is happening through an employee, employee member makes an order on behalf of the customer.
So what I want is that throughout the process flow, like once you're done adding customer details, it goes to ORDER, so I want it to carry the same CustomerID for the customer I'm busy with.
Can someone please help me with a startup code?
Assuming Customer, Order, Invoice and Payment are classes:
Ord = New Order(Cust.CustomerID)
Basically just pass the currently Cust ID to the Order constructor when you are creating it. If you need to create 'blank' orders, overload the constructor:
Class Order
Public Sub New()
...
End Sub
Public Sub New (Cust As Long)
...
End Sub
If that is not possible for some strange reason, make it possible to load a customer via a method:
Ord = New Order()
Ord.LoadCust(Cust.CustomerID)
------------- Edit for procedural version -----------------------
PUBLIC ThisCustID As String ' (?) THIS is a global variable if declared
' outside a SUB like in a module
.... customer does stuff
ThisCustID = ID_OF_NEW_CUST ' what was just saved in db
far away in Order module:
Public Sub MakeNewOrder()
' has access to ThisCustID, but should not change it
End Sub
or:
Public Sub MakeNewOrder(CustID)
' pass the current customer and avoid a global variable.
Rinse and repeat for ThisOrderID to pass to Invoice

Defining objects from data in an excel table

So I have data that is supplier by country where the supplier information changes by country.
600 suppliers and will be upwards of 35 countries(15 attributes per country per supplier). The excel data sheet looks similar to this:
SupplierID SupplierName USsupplierCategory USsupplierCategoryCode UKSupplierCategory ...
1 Sup1 Beverages 1 Ropes
3 Sup5 Ladders 46 Small Ladders
If I could figure out a simple way to get this excel data into an array(even copying and pasting works if the formatting goes quickly since I only have to do this once a month at the most) I can then loop through it an build the needed objects off of the array. But I can't find a simple way to build an array with the excel data without going through a lot of formatting for an array assignment.
I'm pretty new to VB.net and still an amateur programmer and I just can't seem to envision a simple solution to this.
Is an array the way to go? Should I instead loop through each row as a string and break the data by tabs and assign the data that way?
If I am too vague I apologize, let me know and I will provide more specific detail and some code if needed.
The high level approach I'd use is as follows:
1) Define your supplier object
2) Create a supplier object for each row of your datatable and add it to a collection, e.g. a List of supplier objects
Once you have done this, you can easily iterate through your collection of supplier objects to do whatever you like.
Detailed approach:
1) Define your supplier object:
Public Class Supplier
Public property SupplierID as integer
Public Property SupplierName as string
Public Property USsupplierCategory as string
Public property USsuppliercategorycode as integer
Public property UKSupplierCategory as string
End Class
2) Read your excel object into a datatable
This gets a bit tricker. As Tim said, it is easier to save your excel files as csv and then read them in. This is because it is easy to read in csv, but it is also complicated to read in excel files. They can be either xls or xlsx format, and deciding on which one can be a problem. We can also get into COM object issues etc.
If you are to stick with reading in excel, I recommend using Linq2Excel, as this library handles the xls / xlsx issue for you. Here is some example code I knocked up based on your example spreadsheet that returns a list of supplier objects:
Imports LinqToExcel
Imports Remotion.Data.Linq
Imports System.Data
Imports System.Linq
Public Class ReadInExcelData
Public Shared Function GetSupplierListFromSupplierExcel() As List(Of Supplier)
Dim excel As ExcelQueryFactory = New ExcelQueryFactory("C:\Users\YourUserName\Desktop\ExampleData.xls")
Dim suppliers = From c In excel.Worksheet
Select c
Dim list As New List(Of Supplier)
For Each supplier In suppliers
list.Add(New Supplier(supplier.Item(0),
supplier.Item(1),
supplier.Item(2),
supplier.Item(3),
supplier.Item(4)))
Next
Return list
End Function
End Class
You can get the linqtoexcel library here: http://code.google.com/p/linqtoexcel/

Active Record with Entity Framework

I'm working on a project that was built using ADO.NET (raw sql) and Active Record pattern. I am slowly moving it away from ADO.NET to Entity Framework Code First 4.3.
Here is an example of the pattern. The interface is a static Load and an instance Save (and Delete -- not shown).
Public Class Part
Public Property Id as Integer
Public Shared Function Load(_id As Integer) As Part
Using context As New DataContext()
Return context.Find(_id)
End Using
End Function
Public Sub Save()
Using context As New DataContext()
If Id = 0 Then
context.Parts.Add(Me)
Else
context.Entry(Me).State = Data.EntityState.Modified
End If
context.SaveChanges()
End Using
End Sub
End Class
I realize Active Record is not ideal for EF but I'd like to make it work to remove all of the ADO.NET code while not touching the rest of the code.
This mostly works, but I've run into an issue I don't know how to solve. In order to keep Foreign Keys in sync we handle it like such:
Public Sub Save()
ParentPart = Part.Load(ParentPartId)
ChildPart = Part.Load(ChildPartId)
Using context = New iTracContext()
If bid = 0 Then
context.BillOfMaterials.Add(Me)
Else
context.Entry(Me).State = Data.EntityState.Modified
End If
context.SaveChanges()
End Using
End Sub
This makes sure EF doesn't complain that we have non-matching relationships -- the Id always wins.
The issue is that its throwing an exception now when I save.
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
This is thrown from the line:
context.Entry(Me).State = Data.EntityState.Modified
How is anything in the ObjectStateManager for this context? It is brand new and should be empty, no?
If I remove the two Part.Load(...) lines it works fine.
Is there some type of change tracker that lives outside the context that I'm not aware of? That seems like it would kill any attempt at the Active Record pattern.
I'm also open to any suggestions on how to make Active Record work with EF. The context.Entry line is terrible but I don't know what else to do.
Telling me not to do Active Record isn't helpful, but feel free.
I believe Entity Framework may still be tracking the object from the context you loaded it from, because you create a new context for each Load and Save call. If this is the case, try detaching the objects after you load them:
Public Shared Function Load(_id As Integer) As Part
Using context As New DataContext()
Part part = context.Find(_id)
context.Entry(part).State = EntityState.Detached ' Detach from the initial context
Return part
End Using
End Function

DNN Dal+ - retrieve individual info class collection items (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!