I'm working on a project with EF5, Vb.net, AJAX (Javascript), ASMX-Webservices and HTML.
My question is if i have (example classes):
Public Class Company
Public Property CompanyId As Integer
Public Property Name As String
Public Overridable Property Companytype As Companytype
End Class
and the Class:
Public Class Companytype
Public Property CompanytypeId As Integer
Public Property Name As String
-> Public Overridable Property Companies As List(Of Company)
End Class
do I need the -> marked line?
I'm afraid but I really don't know which advance this line brings to me.
Actually I can read all the Companies of a Companytype like this:
Public Shared Function PostCompanyTypeCompanies() As List(Of Company)
Dim db As New Context
Dim x As Integer = 1
Dim y = (From c In db.Companies Where c.CompanyType.CompanyTypeId = x Select New With _
{c.Name, _
c.CompanyType})
Dim z As List(Of Company) = y.AsEnumerable() _
.Select(Function(t) New Company With _
{.Name = t.Name, _
.CompanyType = t.CompanyType}).ToList()
Return z
End Function
This with 'x' is just an example, I can just pass the CompanytypeId to the function.
The problem with the lists is, I always get a circular reference when I want to get the Companytypes for a new Company and I can't access the companytype of a Company like:
Company.Companytype.Name
When I do it without the list everything works fine, because i can store the whole Companytpe to the Company.
I tried the other possibility with setting the Getter of the Child & Parent Properties to Protected then the problem was logically also that I couldn't access the variable as I described a 3 lines above.
So the important question is: Is this -> List Property mandatory?
Thanks for you help.
do I need the -> marked line? NO. It is redundant information (with no real use), which potentially, might provoke consistency errors. Example:
Dim company1 As New Company()
Dim listCompanies As New List(Of Company)()
Dim companiesType1 As New Companytype()
listCompanies.Add(company1)
With companiesType1
.CompanytypeId = 1
.Name = "Type 1"
.Companies = listCompanies
End With
With company1
.CompanyId = 1
.Name = "1"
.Companytype = companiesType1
End With
The code above defines Company1 and the associated type (companiesType1... although we have the problem of "first the egg or the chicken", what gives a first idea of why the chosen approach is erroneous). If you create a new company of the same type:
Dim company2 As New Company()
With company2
.CompanyId = 2
.Name = "2"
.Companytype = companiesType1
End With
You would have to redefine companiesType1 (update its Companies property), to keep the consistency. But, as far as this is redundant information, Companytype would "do the job" independently upon this fact:
If (company2.Companytype Is company1.Companytype) Then
'Both companies have the same type
End If
The aforementioned condition will always be true: either if companiesType1 contains the right information (was updated with company2) or not.
If you need to have a list of all the companies belonging to certain type, you should better rely on a different class storing all the values (e.g., allTheCompanies).
Related
I'm working with a rather complex problem in VBA and I decided to structure it in different classes.
One of the classes containd an array and I need to modify the component individualy outside the class.
The problem can de reduced to the definition of the class
Public a As Variant
Private Sub Class_Initialize()
ReDim a(5) As Double
' Set some value
a(1) = 20
End Sub
And a sub to access the data:
Sub Test()
Dim b As New DummyTest
b.a(4) = 7
Debug.Print (b.a(1))
Debug.Print (b.a(4))
End Sub
The result on the debug window is 20 0. I've checked this result with an inspection and confirm that the property a cannot be modified from the exterior of the class.
Funny part is that the property can be modified if replaced by other array, for example:
Sub Test2()
Dim b As New DummyTest
b.a = Array(1, 2, 3, 4, 5)
Debug.Print (b.a(1))
Debug.Print (b.a(4))
End Sub
Behaves as expected. I'm confused as element access is allowed inside the class context and in any other situation, but in this specific case it doesn't wotk. There is no error msg, it simply refuses to change the content of the array.
I'll find work around solutions but missing this way to access data is being a really PITA.
Hope you can help me with this issue.
Thank you in advance!
Your "property" is breaking encapsulation, which pretty much defeats its entire purpose.
Public a As Variant
From a public interface standpoint, this is a read-write property (if you added a new class module with Implements DummyTest you'd have to add Property Get and Property Let members for it). From the class' standpoint, it's a public instance field.
When a class encapsulates an array, a collection, a dictionary, or any other data structure, the last thing you want is for that data structure to be publicly exposed, with anyone anywhere being able to just overwrite the class' entire internal state as it pleases - you're not getting any of the advantages of classes that way.
First step to proper encapsulation is to make the instance field Private. Next step is to expose actual Property accessors. You want the array data to be writable? Expose an indexed Property Let member for it.
Private a As Variant
Private Sub Class_Initialize()
ReDim a(0 To 10)
End Sub
Public Property Get Value(ByVal index As Long) As Variant
Value = a(index)
End Property
Public Property Let Value(ByVal index As Long, ByVal newvalue As Variant)
a(index) = newvalue
End Property
This code behaves exactly as advertised:
Public Sub Test()
Dim b As DummyTest
Set b = New DummyTest
b.Value(4) = 7
Debug.Print b.Value(1)
Debug.Print b.Value(4)
End Sub
Note that because the array is encapsulated, that client code is absolutely unable to re-assign the array itself: the array is abstracted away and only accessible through the means exposed by the class.
Reference variable in VBA must be declared, so that we know what type of object we are referring to
i.e. Dim b As DummyTest ' No new required in this step
Before using a declared reference to store information create an instance of the Class.
i.e. Set b = New DummyTest
This might seem confusing but is actually very logical. Because b is a Class the Dim statement sets up b to hold the address of a DummyTest object. At the time of the Dim statement no object has been created (no memory allocated) so the address of b is nothing (no value or null). The set statement tells VBA to create an instance of your DummyTest class (allocate the memory it needs) and puts the address of the instance (memory) into variable b. Then because VBA knows that b points to an instance of a class you never see the address of b when you use it, just the data to which b is pointing. You can get the the address b is holding using the function Objptr but this is only something you would do in very special/specific circumstances
I have a winforms applications that has an ms sql server backend. In my database i have lookup tables for like status, and other tables where the data rarely changes. In my application, several forms might use the same lookup tables (Some have a lot of data in them). Instead of loading/filling the data each time the form is open, is there a way to cache the data from the database that can be accessed from multiple forms. I did some searching, but couldnt find the best solution. There is caching, dictionaries, etc. What is the best solution and can you point me to the documentation that discusses it and may even have an example.
Edit:
In my original post I failed to mention that I have a strongly typed dataset and use tableadapters. I want to preload my lookup tables when my application starts, and then have these dataset tables be used throughout the application, on multiple forms without having to fill them on every form.
I have tried creating a class:
Public Class dsglobal
Public Shared EML_StaffingDataSet As EML_StaffingDataSet
Public Shared Sub populateDS()
EML_StaffingDataSet = New EML_StaffingDataSet
End Sub
Public Shared Sub loadskills()
Dim ta As New EML_StaffingDataSetTableAdapters.TSTAFFSKILLTableAdapter
ta.Fill(EML_StaffingDataSet.TSTAFFSKILL)
End Sub
End Class
I run this on a background worker when my application is starting up. So it loads the dataset table. On fill, I can see the datatable has data in it. When I open a form, i want to use the dataset table, but it seems to clear the data out. Not sure if my approach is correct or where my error is.
Edit2:
I have also tried this per comments, but not sure I am doing it correctly. If I am doing it correctly, then how do I use that as a datasource at design time, can i only do that programmatically?
Public Module lookupdata
Private EML_StaffingDataSet As EML_StaffingDataSet
Private skillvalues As List(Of skill)
Public ReadOnly Property skill As List(Of skill)
Get
If skillvalues Is Nothing Then
getskillvalues()
End If
Return skillvalues
End Get
End Property
Private Sub getskillvalues()
skillvalues = New List(Of skill)
EML_StaffingDataSet = New EML_StaffingDataSet
Dim ta As New EML_StaffingDataSetTableAdapters.TSTAFFSKILLTableAdapter
ta.Fill(EML_StaffingDataSet.TSTAFFSKILL)
For Each row As DataRow In EML_StaffingDataSet.TSTAFFSKILL
Dim skill As New skill
skill.skill_id = row("skill_id")
skill.skill_desc = row("skill_desc")
skill.skill_open_ind = row("skill_open_ind")
skillvalues.Add(skill)
Next
End Sub
End Module
Public Class skill
Public Property skill_id As Integer
Public Property skill_desc As String
Public Property skill_open_ind As Boolean
End Class
You might want to consider a lazy loading pattern, like this:
Public Module LookupData
Private statusValues As List(Of LookupValue)
Public Readonly Property Statuses As List(Of LookupValue)
Get
If statusValues Is Nothing Then
GetStatusValues()
End If
Return statusValues
End Get
End Property
Private Sub GetStatusValues()
statusValues = New List(Of LookupValue)
Dim sql = "select key, value from StatusTable"
'TODO: Read the items from the database here, adding them to the list.
End Sub
End Module
Public Class LookupValue
Public Property Key As String
Public Property Value As String
End Class
The idea is that you've got a single instance of LookupData (a Module in VB, there can be only one). Lookup data has a series of Properties, each of which returns a list of values from the database. If the data has already been loaded, it just returns what it has cached. If the data has not been loaded, then the first time it is referenced it retrieves it from the database.
You can consume it elsewhere in your code as follows:
Dim myStatuses = LookupData.Statuses
I am new to VB.NET and WPF.
I am building a "Questionnaire" app. Users will be presented sequentially with different questions/tasks (windows). After they respond on each question/task and press a "submit" button a new window will open with a new question/task, and previous window will close. After each question, when the button is pressed, I need to store data to some global object. After all questions are answered the data of this object should be written out to the output file.
I figured out that Dictionary will be the best to store the results after each window.
I am not sure how, where to create this global Dictionary and how to access it. Should I use View Model? If yes, can you give an example? Or, should it be just a simple class with shared property? (something like this)
EDIT 2: I tried many different ways recommended online
GlobalModule:
Module GlobalModule
Public Foo As String
End Module
GlobalVariables:
Public Class GlobalVariables
Public Shared UserName As String = "Tim Johnson"
Public Shared UserAge As Integer = 39
End Class
Global properties:
Public Class Globals
Public Shared Property One As String
Get
Return TryCast(Application.Current.Properties("One"), String)
End Get
Set(ByVal value As String)
Application.Current.Properties("One") = value
End Set
End Property
Public Shared Property Two As Integer
Get
Return Convert.ToInt32(Application.Current.Properties("Two"))
End Get
Set(ByVal value As Integer)
Application.Current.Properties("Two") = value
End Set
End Property
End Class
Here is where I save the data to global variables/properties in the first window. I need to store data in this subroutine before closing an old window and opening a new window. I use MessageBox just for testing.
Private Sub btnEnter_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnEnter.Click
Dim instructionWindow As InstructionsWindow
instructionWindow = New InstructionsWindow()
Application.Current.Properties("number") = textBoxValue.Text
Globals.One = "2"
Globals.Two = 3
MessageBox.Show("GlobalVariables: UserName=" & GlobalVariables.UserName & " UserAge=" & GlobalVariables.UserAge)
GlobalVariables.UserName = "Viktor"
GlobalVariables.UserAge = 34
GlobalModule.Foo = "Test Foo"
'testing if it saved tha value
'MessageBox.Show(Application.Current.Properties("number"))
Application.Current.MainWindow.Close()
instructionWindow.ShowDialog()
End Sub
Next subroutine is where I am trying to retrieve the value from global Properties/variables in the second window, but message boxes come out empty. There might also the case that I am assigning values in a wrong way, or not reading them in a right way (casting?) :
Private Sub FlowDocReader_Initialized(ByVal sender As Object, ByVal e As System.EventArgs) Handles FlowDocReader.Initialized
' Get a reference to the Application base class instance.
Dim currentApplication As Application = Application.Current
MessageBox.Show(currentApplication.Properties("number"))
MessageBox.Show("One = " & Globals.One & " Two = " & Globals.Two)
MessageBox.Show("GlobalVariables: UserName=" & GlobalVariables.UserName & " UserAge=" & GlobalVariables.UserAge)
MessageBox.Show("GlobalModule.Foo = " & GlobalModule.Foo)
Dim filename As String = My.Computer.FileSystem.CurrentDirectory & "\instructions.txt"
Dim paragraph As Paragraph = New Paragraph()
paragraph.Inlines.Add(System.IO.File.ReadAllText(filename))
Dim document As FlowDocument = New FlowDocument(paragraph)
FlowDocReader.Document = document
End Sub
Thanks.
You can make public Dictionary property for form and put your dictionry to this property or make constructor with Dictionary argument.
You already have this dictionary Application.Properties
Look here, please.
First, you can define a dictionary (list of lists) as follows at the beginning of a form or in a module
Dim dic As New Dictionary(Of String, List(Of String))
As the user completes questions on a form, write the partucular form number and query results to a single record in the dic before going to the next form (place this code into the "Next" button):
'Assume q1response=3, q2response=4,..., qpresponse="text", etc.
Dim myValues As New List(Of String)
myValues.Add(formname)
myValues.Add(q1response)
myValues.Add(q2response)
.
.
myValues.Add(qpresponse)
dic.Add(username, myValues)
When a user is done, there will be multiple records in the dictionary, each of which starts with their name and is followed by question responses. You can loop through multiple dictionary records, where each record is for a user using the following:
For Each DictionaryEntry In dic 'this loops through dic entries
Dim str As List(Of String) = DictionaryEntry.Value
'here you can do whatever you want with results while you read through dic records
'username will be = str(0)
'formname will be str(1)
'q1 response on "formname" will be str(2)
'q2 response on "formname" will be str(3)
'q3 response on "formname" will be str(4)
...
Next
The trick is that there will be multiple dictionary records with results for one user, where record one can have results like "John Doe,page1,q1,q2,q3" and record 2 will be "John Doe,page2,q4,q5,q6." Specifically, the "str" in the above loop will be an array of string data containing all the items within each dictionary record, that is, in str(0), str(1), str(2),... This is the information you need to work with or move, save, analyze, etc.
You can always put all the code I provided in a class (which will be independent of any form) and dimension the sic is a Sub New in this class, with the updating .Add values lines in their own sub in this same class). Then just Dim Updater As New MyNewClassName. Call the Updater in each continue button using Call Updater.SubNameWithAddValues(q1,q2,...qp). It won't matter where you are in your program since you using a specific class. The one thing I noticed with my code is that you can only use the line that adds the "key" or the username once, so use it after the last query -so put it in a Sub Finished in your new class and call as Call Updater.Finished(username,q30,q31,last)
I have an ObservableCollection of class Customers that I fill from a database query. Because of how the tables are joined, I sometimes get one off entries (Bill has a shipping address that is different than his mailing address) that show up in the data twice.
Each customer row has an ID as unique primary key, and this is what I use to pull up more customer information when someone selects a row from the bound ListView.
In the WinForms version of this program, I would search the ListView for the CustomerID, and if it was found I would avoid inserting it a second time.
ObservableCollection doesn't seem to have the ability to easily tell me if a CustomerID already exists in one of the class instances of the collection, so I'm wondering what the best way to handle this is.
The ideas I've had so far:
' Not sure how to make this work, since the CustomerID and Name would be the same, but the city, state, zip might not be.'
t = new classCustomer(CustomerID, CustomerName, City, State, Zip)
if not sr.contains(t) then
sr.Add(t)
end if
Possibly figure out how to create an ObservableDictionary, but so far all the examples are in C#, and it may take me a while to port it over to VB.net
Anyone know of a better implementation?
You simply need to tell .NET what defines a person, in this case the ID.
Simply override Equals on your customer object, then your collection will now be able to know if 2 customers are equivalent:
Public Class Person
Private id As Integer
Public Sub New(ByVal id As Integer)
Me.id = id
End Sub
Public Overrides Function Equals(ByVal obj As Object) As Boolean
Return (TypeOf (obj) Is Person) And (DirectCast(obj, Person)).id = Me.id
End Function
Public Overrides Function GetHashCode() As Integer
Return Me.id.GetHashCode()
End Function
End Class
Sub Main()
Dim observable As New ObservableCollection(Of Person)()
observable.Add(New Person(1))
Dim duplicate As New Person(1)
If Not observable.Contains(duplicate) Then
observable.Add(duplicate) ' never gets hit because of .Equals override
End If
End Sub
without the override it does not know how to tell if they are equivalent or not.
Its been a while since I've worked with ASP, but I'm getting a Type mismatch error on what seems to be a simple assignment statement. Can anyone shed some light on why this might be happening.
This works, but when I try to foreach an unassigned Questions block I get an Object not a collection error
Class Survey
public ID
public Title
public Questions
End Class
Sub Test()
Dim oSurvey
Set oSurvey = new Survey
Dim aQuestions(2)
Set aQuestions(0) = new Question
' Other aQuestions assignments
oSurvey.Questions = aQuestions
End Sub
Alternately If I setup questions as a dynamic array then the assignment fails with a type mismatch error.
Class Survey
public ID
public Title
public Questions()
End Class
Sub Test()
Dim oSurvey
Set oSurvey = new Survey
Dim aQuestions(2)
Set aQuestions(0) = new Question
' Other aQuestions assignments
' Throws a Type mismatch error here
oSurvey.Questions = aQuestions
End Sub
Any thoughts?
To answer your question as to what is actually going on.
when I try to foreach an unassigned
Questions block I get an Object not a
collection error
For Each enumerates a set of variants from the source variable, it does this by acquiring an IEnumVARIANT. If the source variable holds an object it is expected to have an implementation of this interface. If it is an array VBScript creates an implementation dynamically and it can only do this if the array has been dimensioned. Anything else in the source variable (such as Empty in this case) will result in an error.
then the assignment fails with a type mismatch error.
The left hand side of an assignment operation must always be a variant. Hence its not possible copy the contents of one dynamic array to another via a simple assignment.
Your first approach is reasonably sound but you need a way to represent an empty array without crashing out a For Each. You can use this little trick:-
Function EmptyArray
EmptyArray = Split("", " ")
End Function
Class Survey
public ID
public Title
public Questions
Private Sub Class_Initialize
Questions = EmptyArray
End Sub
End Class
Now if you try to For Each the Questions before it has been assigned a real array the for each will do nothing as expected. Also if you use UBound(Questions) + 1 to get the count of questions that will still be accurate since UBound(EmptyArray) is -1.
If I try to paste the same code into VBA (excel or word), it doesn't compile.
It shows error on line public Questions() saying
---------------------------
Microsoft Visual Basic
---------------------------
Compile error:
Constants, fixed-length strings, arrays, user-defined types and Declare statements not allowed as Public members of object modules
---------------------------
OK Help
---------------------------
In the first example, you haven't defined it as an array (it is a variant & hence can be assigned any value).
Update: I tested this in ASP.NET not realizing the question was about classic ASP. I've modified the code below to work with classic ASP, though I haven't been able to test it yet:
Class Question
...
End Class
Class Survey
Public ID
Public Title
Public Questions As Question()
End Class
Sub Test()
Dim oSurvey As New Survey
Dim aQuestions(0 To 2) As Question
Set aQuestions(0) = New Question
...
Set oSurvey.Questions = aQuestions
End Sub
In your first example, Survey.Questions isn't a collection; in your second, it is an array of Type Variant.
So I ended up sticking with the Array declaration. Additionally, When I tried to ReDim the array like so I got an error.
ReDim oSurvey.Questions(2)
So I created a sub routine to ReDim the Array, and this worked.
Class Survey
public ID
public Title
public Questions()
sub ReDimQuestions(count)
ReDim Questions(count)
end sub
End Class
Sub Test()
Dim oSurvey
Set oSurvey = new Survey
oSurvey.ReDimQuestions 2
Set oSurvey.Questions(0) = new Question
' Other aQuestions assignments
End Sub