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
Related
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.
When using vb.net, if code is contained inside "< >" signs, like a namespace, what is it telling the compiler to do? Also, what would these be signs be called when used like this?
To give clarity to the question; I know that parentheses "( )" are generally used for arguments and that brackets "[ ]" are used to declare a new type, but I cannot find what the less than/greater than signs do when used in a similar capacity.
I've looked through my reference books and attempted to research this through the internet but I haven't come up with an answer. Most likely because I don't know what exactly these would be named. I always results that talk about the relational operators, which is not what I'm looking for.
Here is an example of what I'm looking at:
Imports System.ComponentModel.Design
'<CLSCompliant(True)>
<System.ComponentModel.DefaultEvent("DataReceived")> _
Public Class SerialDF1forSLCMicroCon
Inherits MfgControl.AdvancedHMI.Drivers.DF1ForSLCMicroPLC5
Implements System.ComponentModel.IComponent
Implements System.ComponentModel.ISupportInitialize
Private Shared ReadOnly EventDisposed As New Object()
Public Event Disposed As EventHandler Implements System.ComponentModel.IComponent.Disposed
Protected m_synchronizationContext As System.Threading.SynchronizationContext
Specifically I am looking at the line that contains
<System.ComponentModel.DefaultEvent("DataReceived")> _
That is an attribute. It is a way of attaching metadata (additional information) to your code that can be queried later using reflection.
For example, let's say you have a series of classes (e.g. Customer, Contact, Order, Product, etc.), each of which corresponds to a database table, and inherits from a DbTable base class that has a common DeleteAll() method.
Now, it might be that your database table names don't match your class names. In that case you can define an attribute that adds additional information to your class, providing the table name, as shown here:
<DbTableName("CUST01")>
Public Class Customer
Inherits DbTable
...
End Class
This indicates that your "Customer" objects are stored in the "CUST01" table in the database.
You might implement the attribute like this:
Public Class DbTableNameAttribute
Inherits System.Attribute
Public Property Name As String
Public Sub New(value As String)
Name = value
End Sub
End Class
Lastly, in your base DbTable class, you would implement DeleteAll() like this:
Public MustInherit Class DbTable
Public Sub DeleteAll()
' Use reflection to retrieve the attribute.
Dim attributes = Me.GetType().GetCustomAttributes()
Dim dbTableNameAttribute = attributes.FirstOrDefault(Function(x) x.GetType() = GetType(DbTableNameAttribute)
If dbTableNameAttribute IsNot Nothing Then
Dim tableName As String = CType(dbTableNameAttribute, DbTableNameAttribute).Name
' tableName will contain the value specified in the attribute (e.g. "CUST01")
Dim sql As String = "delete from " & tableName
' ... at this point you would send the delete command to your database ...
End If
End Sub
End Class
Now, in the specific example you cite: <System.ComponentModel.DefaultEvent("DataReceived")>
What is likely happening is that the SerialDF1forSLCMicroCon class probably has multiple events, and the attribute is providing a hint to the designer that the "DataReceived" event is the default one. You'll see a similar sort of thing with a Windows Forms Button. If you click the events for a Button, there are many, but the "Click" event is always highlighted by default, as it is the most commonly used one.
I have a nightmarishly complicated set of Excel macros that I'm trying to simplify by setting up a series of classes, but I haven't ever done object-oriented programming before, so I'm a little unclear as to how to set things up (or even how to formulate my question). I currently have a Lease class which has a Payments property, like so:
Option Explicit
Private oPayments As New Payments
Public Property Get Payments() As Payments
Set Payments = oPayments
End Property
Public Property Let Payments(param_Payments As Payments)
Set oPayments = param_Payments
End Property
I also have a Payment class, which will hold things like the payment number, and a Payments class, which will hold instances of the Payment class. The Payments class looks like this:
Option Explicit
Private Payments As New Collection
Sub Add(param_Number As String)
Dim NewPayment As Payment
Set NewPayment = New Payment
NewPayment.PaymentNumber = param_Number
Payments.Add NewPayment
End Sub
Property Get Count() As Long
Count = Payments.Count
End Property
Property Get Item(Index As Variant) As Payment
Set Item = Payments(Index)
End Property
This all works pretty well, but it's a little bit clunky, since in order to specify a payment, I need to use Lease.Payments.Item(1).PaymentNumber. How could I set things up so I could just use Lease.Payments(1).PaymentNumber? Better yet, since there's only one lease per worksheet, can I set it up so that I just say Payments(1).PaymentNumber and it defaults to the lease on the active sheet, the way that built-in objects like Range do? I could just rename "Payments" as "Lease" and "Item" as "Payment", but then to add a Payment, I would do Lease.Add, which isn't very intuitive. I'd rather add a payment by using Lease.Payments.Add, to keep things consistent with the built-in classes, but I'm not sure how to do that.
Thanks!
The answer to creating a "default property" for your class can be found here. It's kinda ugly, but it can be done.
I don't have a great answer for the second question other than to advise you to just create a variable in a module and set it on initialization:
Set Payments = Lease.Payments
For the second part I don't think you can do it like the Range object. You could make a Leases collection class. Assume gLeases is a public variable holding all your leases.
Dim Payments as CPayments
Set Payments = gLeases.GetLeaseBySheet(ActiveSheet).Payments
Debug.Print Payments(1).PaymentNumber
I don't know how your filling your leases now, but you would need to load them all up so that your GetLeaseBySheet property can find the right one.
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
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!