VB.NET How To Prevent Infinite Recursion During Object Population - vb.net

I'm a bit stuck right now in trying to determine the best solution to prevent an infinite recursion loop. Perhaps it's not exactly "recursion", but it's a set of function calls that I can pretty much guarantee will be calling each other indefinitely if I can't come up with a solution.
In trying to figure out how to explain the issue, it seems the best way I can come up with is to start with some simplified (and redacted) code. For this example, I'll use a Classroom and a Student.
Public Class Classroom
Public Property ClassroomID As Integer
Public Property ClassroomDescription As String
Public Property Students As List(Of Student)
Public Sub New(ByVal ClassroomID As Integer)
Initialize()
GetClassroomDetail(ClassroomID)
End Sub
Public Sub GetClassroomDetail(ByVal ClassroomID As Integer)
Dim Reader As SqlDataReader
' HERE'S WHERE I MAKE THE DATABASE CALL TO
' GET THE CLASSROOM RECORD DETAILS
FillClassroomRecord(Reader)
End Sub
Private Sub FillClassroomRecord(Reader)
While Reader.Read
ClassroomID = CType(Reader("ClassroomID"), Integer)
ClassroomDescription = CType(Reader("ClassroomDescription"), String)
Students = GetClassroomStudents(ClassroomID)
End While
End Sub
Private Function GetClassroomStudents(ByVal ClassroomID As Integer) As List(Of Student)
Dim StudentData As DataTable
Dim ClassroomStudents As New List(Of Student)
' I PULL A LIST OF STUDENTS RELATED TO THE SPECIFIC CLASSROOMID
For Each StudentRow As DataRow In StudentData.Rows
Dim NewStudent As New Student(CType(StudentRow("studentid"), Integer))
ClassroomStudents.Add(NewStudent)
Next StudentRow
Return ClassroomStudents
End Function
End Class
So far, pretty straight forward. However, the problem comes in the fact that the same student may be tied to multiple classrooms. I want to have a similar method in the Student object to be able to pull all related classrooms for that student.
Public Class Student
Public Property StudentID As Integer
Public Property Name As String
Public Property Classrooms As List(Of Classroom)
...
Private Function GetStudentClassrooms(ByVal StudentID As Integer) As List(Of Classroom)
Dim ClassroomData As DataTable
Dim StudentClassrooms As New List(Of Classroom)
' PULL A LIST OF CLASSROOMS RELATED TO THE SPECIFIC STUDENTID
For Each ClassroomRow As DataRow In ClassroomData.Rows
Dim NewClassroom As New Classroom(CType(ClassroomRow("classroomid"), Integer))
StudentClassrooms.Add(NewClassroom)
Next ClassroomRow
Return StudentClassrooms
End Function
End Class
So, my consternation at this point is, how do I prevent it from constantly looping back and forth between the classrooms and the students populating the same things over and over again in an infinite loop?
The only thing I can think to do is to set a Boolean variable somewhere that I can set to identify whether or not to keep drilling down. The problem is that my brain is a bit fried at the moment, and I can't figure out how to implement such a solution.
Some of the searching I've done also mentioned the possibility of some sort of "backtracking", which sounds cool, but doesn't seem very likely to work in this type of situation. Of course, I could be wrong on that, and I'd love to see any sort of implementation that is somehow capable of intelligently identifying if I'm just looping the same things over and over again.
What I'd like to see happen is the top-level Classroom object creation should pick up its Student objects, which should pick up any additional Classroom objects to which they are associated, including each of those Classroom object's Student objects, and then it stops.
I hope that all makes sense. It's actually even more complicated than this as there are other similar objects that will be tied back to the top-level object (Classroom), which should also follow the same basic rules - don't dig too deep into the "sub" records, and prevent an infinite recursive loop
If any clarification is necessary, please let me know. I truly appreciate any assistance you can provide.

I think I may have an idea of how to solve this, but I'd like to get some feedback on my idea.
If I create an overload of the New constructor that accepts a boolean value (GetRelatedDetails), then I can call it initially with that flag set to True. Just about everything else should remain the same, other than passing this value down the chain. Using the above example, it would look something like this:
Public Class Classroom
Public Property ClassroomID As Integer
Public Property ClassroomDescription As String
Public Property Students As List(Of Student)
Public Sub New(ByVal ClassroomID As Integer)
Initialize()
GetClassroomDetail(ClassroomID, False)
End Sub
' OVERLOAD WITH BOOLEAN VALUE TO GET RELATED DETAILS
Public Sub New(ByVal ClassroomID As Integer, ByVal GetRelatedDetails As Boolean)
Initialize()
GetClassroomDetail(ClassroomID, GetRelatedDetails)
End Sub
Public Sub GetClassroomDetail(ByVal ClassroomID As Integer, ByVal GetRelatedDetails As Boolean)
Dim Reader As SqlDataReader
' HERE'S WHERE I MAKE THE CALL TO GET THE
' CLASSROOM RECORD DETAILS FROM THE DATABASE
FillClassroomRecord(Reader, GetRelatedDetails)
End Sub
Private Sub FillClassroomRecord(ByVal Reader As SqlDataReader, ByVal GetRelatedDetails As Boolean)
While Reader.Read
ClassroomID = CType(Reader("ClassroomID"), Integer)
ClassroomDescription = CType(Reader("ClassroomDescription"), String)
If GetRelatedDetails Then
Students = GetClassroomStudents(ClassroomID)
End If
End While
End Sub
Private Function GetClassroomStudents(ByVal ClassroomID As Integer) As List(Of Student)
Dim StudentData As DataTable
Dim ClassroomStudents As New List(Of Student)
' I PULL A LIST OF STUDENTS RELATED TO THE SPECIFIC CLASSROOMID
For Each StudentRow As DataRow In StudentData.Rows
Dim NewStudent As New Student(CType(StudentRow("studentid"), Integer))
ClassroomStudents.Add(NewStudent)
Next StudentRow
Return ClassroomStudents
End Function
End Class
Then the Student object does basically the same thing:
Public Class Student
Public Property StudentID As Integer
Public Property StudentName As String
Public Property Classrooms As List(Of Classroom)
Public Sub New(ByVal StudentID As Integer)
Initialize()
GetStudentDetail(StudentID, False)
End Sub
' OVERLOAD WITH BOOLEAN VALUE TO GET RELATED DETAILS
Public Sub New(ByVal StudentID As Integer, ByVal GetRelatedDetails As Boolean)
Initialize()
GetStudentDetail(StudentID, GetRelatedDetails)
End Sub
Public Sub GetStudentDetail(ByVal StudentID As Integer, ByVal GetRelatedDetails As Boolean)
Dim Reader As SqlDataReader
' HERE'S WHERE I MAKE THE CALL TO GET THE
' STUDENT RECORD DETAILS FROM THE DATABASE
FillStudentRecord(Reader, GetRelatedDetails)
End Sub
Private Sub FillStudentRecord(ByVal Reader As SqlDataReader, ByVal GetRelatedDetails As Boolean)
While Reader.Read
StudentID = CType(Reader("StudentID"), Integer)
StudentName = CType(Reader("StudentName"), String)
If GetRelatedDetails Then
Classrooms = GetStudentClassrooms(StudentID)
End If
End While
End Sub
Private Function GetStudentClassrooms(ByVal StudentID As Integer) As List(Of Classroom)
Dim ClassroomData As DataTable
Dim StudentClassrooms As New List(Of Classroom)
' PULL A LIST OF CLASSROOMS RELATED TO THE SPECIFIC STUDENTID
For Each ClassroomRow As DataRow In ClassroomData.Rows
Dim NewClassroom As New Classroom(CType(ClassroomRow("classroomid"), Integer))
StudentClassrooms.Add(NewClassroom)
Next ClassroomRow
Return StudentClassrooms
End Function
End Class
You'll notice that, even if I pass in a value of True to the initial object, when it gets to the Fill method, and steps into the GetClassroomStudents or GetStudentClassrooms, it just calls the New constructor overload that defaults to False. This way, I'm guessing that it should prevent it from looping infinitely back through the same records over and over again.
Of course, I'm open to any other suggestions or ideas on the best way to implement this, but I think this is what I'm going to try to do for now.

Related

How to serialize a List(of Object) in VB.NET?

I have a class Player that I use to create a List(Of Player). I need to save it when the application closes. In Windows Forms, I would just serialize, but that's no longer possible in UWP, so I had to Google for a few dozen of hours and I finally stumbled upon Microsoft.Toolkit.Uwp, then Newtonsoft.Json, but I fail miserably to use them. I need your help!
Let's say I have a small class :
Dim Name As String
Dim Score As Double
Public Class Player
<JsonConstructor()>
Public Sub New(Name As String, Score As Double) ' New with score
Me.Name = Name
Me.Score = Math.Max(1, Score)
End Sub
End Class
Public Overrides Function ToString() As String ' ToString
Return $"{Name} [{Score}]"
End Function
How do I successfully read and write a List(Of Player)?
' Loading MainPage.xaml
Private Sub MainPage_Loading() Handles Me.Loading
ReadAsync()
MainFrame.Margin = New Thickness(0)
Window.Current.Content = MainFrame
MainFrame.Navigate(GetType(PlayerList), Players)
End Sub
' Read
Private Async Sub ReadAsync()
Players = JsonConvert.DeserializeObject(Of List(Of Player))(Await FileIO.ReadTextAsync((Await StorageFolder.CreateFileAsync("players.json", CreationCollisionOption.OpenIfExists))))
If Players Is Nothing Then
Players = New List(Of Player)
WriteAsync()
End If
End Sub
' Write
Public Shared Async Sub WriteAsync()
Await FileIO.WriteTextAsync(Await StorageFolder.CreateFileAsync("players.json", CreationCollisionOption.ReplaceExisting), JsonConvert.SerializeObject(Players, Formatting.Indented))
End Sub
' Loading PlayerList.xaml
Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
ListBoxPlayers.Items.Clear()
Players = e.Parameter
For Each Player In Players
ListBoxPlayers.Items.Add(Player)
Next
End Sub
' Adding a new player in the interface
Private Sub ButtonAddPlayer_Click(sender As Button, e As RoutedEventArgs) Handles ButtonAddPlayer.Click
' ...
' Commit
MainPage.Players.Add(New Player(TextBoxName.Text))
ListBoxPlayers.Items.Add(MainPage.Players(MainPage.Players.Count - 1))
MainPage.WriteAsync()
End Sub
So this is all confusing. When I add a player trough the interface, it enters my ListBox like normal. However, when I close the application and I re-open it, I get a handful of empty objects.
I did some angry testing to know more about my problem, turns out I'm not serializing at all, but I probably am deserializing correctly.
I found it, turns out it's in my class Player.
What I used :
Dim Name As String
Dim Score As Double
What worked :
Public Name As String
Public Score As Double
What I should have done :
Public Property Name As String
Public Property Score As Double
I was taught to never set variables as "public" when coding in Java, and I didn't know that Property existed in Visual Basic.

Collection class of specific type containing basic features

Every time i use some class e.g Artikel as follows:
Public Class Artikel
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
End Class
For such classes i would like to have collection class. The features i would like to have is like:
--> Add (passing Artikel object)
--> Remove (passing Artikel object)
--> Sort entire collection (based on Position property desc/asc)
--> Compare two Artikels (pass by Artikels and tell by which property has to be compared)
--> Check whether two artikels equals
--> Every added artikel has to be marked by Key (so maybe dictionary)? <key><Artikel>
--> Remove Artikel (passing by Key index)
Could somone from you there tell me or even better provide example of collection class pass those requirments?
EDIT: Startup:
Artikel's collection:
Option Strict On
Public Class Articles
Public Property collection As Dictionary(Of Integer, Artikel)
Sub New()
'Initiate new collection
collection = New Dictionary(Of Integer, Artikel)
End Sub
'Add new Artikel to collection
Public Function AddToCollection(ByVal artikel As Artikel) As Boolean
collection.Add(artikel)
Return True
End Function
'Remove specific Artikel
Public Sub RemoveFromCollectionByArtikel(artikel As Artikel)
If Not IsNothing(collection) Then
collection.Remove(artikel)
End If
End Sub
'Get collection
Public Function GetCollection() As Dictionary(Of Integer, Artikel)
Return collection
End Function
'Sort collection by property position
Public Sub SortByPosition()
collection.Sort()
End Sub
'Remove specific sending keys and then reorder them
Public Sub RemoveAllMarkedAsDeleted(keys As List(Of Integer))
'-- Check whther anything has been marked as deleted
If keys.Count > 0 Then
For Each row In keys
collection.Remove(row)
Next
ReorderKeys()
End If
'Reorder all Artikels in collection
Private Sub ReorderKeys()
Dim newCollection As New Dictionary(Of Integer, Artikel)
Dim index As Integer = 0
For Each collitem In collection
newCollection.Add(index, collitem.Value)
index += 1
Next
collection.Clear()
collection = newCollection
End Sub
End Class
Artikel class (additionally i implemented IComparable to be able to sort)
Option Strict On
Public Class Artikel
Implements IComparable(Of Artikel)
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
Public Function CompareTo(pother As Artikel) As Integer Implements IComparable(Of Artikel).CompareTo 'we can sort because of this
Return String.Compare(Me.Position, pother.Position)
End Function
Public Shared Function FindPredicate(ByVal partikel As Artikel) As Predicate(Of Artikel)
Return Function(partikel2 As Artikel) partikel.ID = partikel2.ID
End Function
Public Shared Function FindPredicateByUserId(ByVal partikel As String) As Predicate(Of Artikel)
Return Function(partikel2 As Artikel) partikel = partikel2.ID
End Function
End Class
Parts of it look good, but I would ultimately do it a bit differently. First, consider overloads on the item class to make them easier to create and default initialization:
Public Class Article
Property ID As Integer = -1
Property Key As String = ""
Property Name As String = ""
Property Position As Integer = -1
Property PubDate As DateTime = DateTime.Minimum
Public Sub New()
End Sub
' whatever minimum data a new item requires
Public Sub New(k As String, n As String)
Key = k
Name = n
End Sub
' full initialization:
Public Sub New(k As String, n As String, pos As Int32,
pubDt As DateTime)
...
End Sub
End Class
I added some properties for variety, and I suspect "Nummer" might be the "Key" mentioned in the OP, but whatever it is, I would add it to the Article class as that name, if it has some importance.
You might need a simple ctor for serialization (???). Some of these will find and use a Private parameterless constructor, but your code will be forced to use one of the overloads in order to provide some minimum level of data when a new one is created.
You probably do not need IComparable. That is typically for more complex comparisons, such as multiple or complex properties. An example is a carton or box:
If (width = Other.Width) AndAlso (height = Other.Height) Then
Return 0
ElseIf (width = Other.Height) AndAlso (height = Other.Width) Then
Return 0
End If
Plus more gyrations to work out which is "less" than the other. One reason you dont need it, is because If Art1.Postion > Art2.Postion is trivial. The other reason in your case, is because a Dictionary cannot be sorted.
Rather than a Dictionary, an internal List would work better for some of the things you describe but still allow you to have it act like a Dictionary to the extent you need it to. For this, I might build it using ICollection<T>:
Public Class ArticleCollection
Implements ICollection(Of Article)
Pressing Enter after that line will add all the required methods including:
Public Sub Add(item As Article) Implements ICollection(Of Article).Add
Public Sub Clear() Implements ICollection(Of Article).Clear
Public Function Contains(item As Article) As Boolean Implements ICollection(Of Article).Contains
Public ReadOnly Property Count As Integer Implements ICollection(Of Article).Count
Public Function Remove(item As Article) As Boolean Implements ICollection(Of Article).Remove
It remains completely up to you how these are implemented. It also doesn't rule out adding methods such as RemoveAt(int32) or RemoveByKey(string) depending on what you need/how it will be used. One of the benefits to ICollection(Of T) is that it includes IEnumerable which will allow use for each loops (once you write the Enumerator): For Each art In Articles
To emulate a dictionary to allow only one item with a specific property value:
Public Class ArticleCollection
Implements ICollection(Of Article)
Private mcol As List(Of Article)
...
Public Sub Add(item As Article) Implements ICollection(Of Article).Add
' check for existing key
If KeyExists(item.Key) = False Then
mcol.Add(item)
End If
End Sub
You can also overload them:
' overload to match Article ctor overload
Public Sub Add(key As String, name As String)
If KeyExists(key) = False Then
' let collection create the new item
' with the minimum required info
mcol.Add(New Article(key, name))
End If
End Sub
If you add an Item Property, you can index the collection ( Articles(3) ):
Property Item(ndx As Int32) As Article
Get
If ndx > 0 AndAlso ndx < mcol.Count Then
Return mcol(ndx)
Else
Return Nothing
End If
End Get
Set(value As Article)
If ndx > 0 AndAlso ndx < mcol.Count Then
mcol(ndx) = value
End If
End Set
End Property
' overload for item by key:
Public Property Item(key As String) As Article
An Add method and an Item Property will be important if the collection will display in the standard NET CollectionEditor.
There are several ways to implement sorting. The easiest is to use linq in the code which uses your collection:
Articles = New ArticleCollection
' add Article items
Dim ArticlesByDate = Articles.OrderBy(Function(s) s.PubDate).ToList()
Where PubDate is one of the Article properties I added. The other way to handle sorting is by the collection class returning a new collection (but it is so simple to do, there is little need for it):
Friend Function GetSortedList(bSortAsc As Boolean) As List(Of Article)
If bSortAsc Then
Return mcol.OrderBy(Function(q) q.PubDate).
ThenBy(Function(j) j.Position).ToList()
Else
Return mcol.OrderByDescending(Function(q) q.PubDate).
ThenByDescending(Function(j) j.Position).ToList()
End If
End Function
Whether it implements ICollection(Of T), inherits from ICollection(Of T) or does work off a Dictionary depends entirely on what this is, how it is used and whatever rules and restrictions there are (including if it will be serialized and how). These are not things we know.
MSDN has an article on Guidelines for Collections which is excellent.
Create your class
Public Class Artikel
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
sub new (_ID as integer, _Nummer as string, _Name as string, _Position as integer)
ID = _ID
Nummer = _Nummer
Name = _Name
Position = _Position
End Sub
End Class
Create another class which holds a private list and add sub routines to it
Public Class ArtikelList
Private _List as new list (of Artikel)
Public sub remove(Key as integer)
Dim obj as Artikel = nothing
for each x as Artikel in _List
if x.ID = Key then
obj = x
exit for
end if
Next
if not isnothing(obj) then
_List.remove(obj)
end if
End sub
Sub Add(obj as Artikel)
Dim alreadyDeclared as boolean = falsse
for each x as Artikel in _List
if x.ID = obj.id then
alreadyDeclared = true
exit for
end if
Next
if not AlreadyDeclared then
_List.add(obj)
Else
'Somehow inform the user of the duplication if need be.
end if
End sub
End Class
Then use your list class.
dim L as new ArtikelList
L.add(new Artikel(1280, "AFKforever!", "Prof.FluffyButton", 96))
L.remove(1280)
I only added one sub routine as an example. I hope it helps but feel free to ask for more example routines.
This can also be done by creating a class which inherits from the list class, exposing all list class functionality but by using this method you are forced to create every subroutine that will be used. This way you only use routines that you created exclusively for the purpose Artikel objects handling.
Check if two Artikels are equal
Public Class Artikel
Property ID As Integer
Property Nummer As String
Property Name As String
Property Position As Integer
sub new (_ID as integer, _Nummer as string, _Name as string, _Position as integer)
ID = _ID
Nummer = _Nummer
Name = _Name
Position = _Position
End Sub
End Class
Public Overrides Overloads Function Equals(obj As Object) As Boolean
If obj Is Nothing OrElse Not Me.GetType() Is obj.GetType() Then
Return False
else
dim _obj as artikel = obj
if Me.ID = _obj.ID then
Return true
else Return False
End If
End Function
End Class
Use it like:
If x.equals(y) then
'they have the same ID
end if

Casting substrings with linq into a list of object and than sorting it base on property in vb.net

This have to be in vb.net linq, i'm pretty sure I could do it in c#, but I cant find any good enough translator to help me ... even the answers I find here in SO seems to only be written in linq, hence the question which might be a duplicate of a c# one.
That being said, considering these 2 classes :
Public class User
Public Property Name() As String
Public Property Teams As TeamList
Public sub New(d as string, results as TeamList)
me.name = d
me.Teams = results
end sub
end class
Public Class TeamList
Public Property TeamName() As String
Public Property fullscore() As list(of object)
Public sub New(name as string, value as list(of string))
me.TeamName = name
me.fullscore = value
me.fullscore = getFullScore(value) (return a list of object)
end sub
End Class
I'm struggling in the final steps of my linq -to - object : (you can copy /paste this in linqpad)
Sub Main
dim Definition as new Dictionary(of String, object)
definition.add("user1_redTeam-02", new object)
definition.add("user1_redTeam-01", new object)
definition.add("user1_blueTeam-03", new object)
definition.add("user2_redTeam-01", new object)
definition.add("user1_redTeam-03", new object)
definition.add("user1_blueTeam-01",new object)
definition.add("user2_blueTeam-01", new object)
definition.add("user1_blueTeam-02", new object)
definition.add("user2_redTeam-02", new object)
Dim q3 = (From userlists In Definition.Keys.GroupBy(Function(s) s.Split("_")(0)) _
Select New With _
{.UserName = userlists.Key, _
.animationList = (From scList In userlists.GroupBy(Of String)(Function(s) s.Split("-")(0)) _
Select New With {.Team = scList.Key, _
.Score = scList.ToList()})})
q3.dump()
End Sub
this is the result :
now, all I want is to sort the .score attribute (just a simple .sort(), and instead of returning an anonymous q3 object, which I,m cluless to transform, I'd like the q3 to be a list(of User)
it think it should looks like this ... but I cant make it works, i always gets some linq conversion errors :
Unable to cast object of type 'WhereSelectEnumerableIterator2[System.Linq.IGrouping2[System.String,System.String],UserQuery+User]' to type 'System.Collections.Generic.List`1[UserQuery+User]'.
Dim q3 as List(of User)= (From userlists In Definition.Keys.GroupBy(Function(s) s.Split("_")(0)) _
Select New User(userlists.Key, (From scList In userlists.GroupBy(Of String)(Function(s) s.Split("-")(0)) _
Select New TeamList(scList.Key, scList.ToList()))))
Your code examples seem to be incorrect - for example, it seems like User.Teams should be a list of some type, not a TeamList object, which isn't really a list. Anyway, with a little modification, this is what I came up with - maybe it's close to what you were looking for (a list of users with the scores sorted). You can paste into LINQPad to run it.
Sub Main
Dim Definition As New Dictionary(of String, Object)
definition.add("user1_redTeam-02", New Object)
definition.add("user1_redTeam-01", New Object)
definition.add("user1_blueTeam-03", New Object)
definition.add("user2_redTeam-01", New Object)
definition.add("user1_redTeam-03", New Object)
definition.add("user1_blueTeam-01",New Object)
definition.add("user2_blueTeam-01", New Object)
definition.add("user1_blueTeam-02", New Object)
definition.add("user2_redTeam-02", New Object)
Dim q3 = (
From userlists In Definition.Keys.GroupBy(Function(s) s.Split("_"c)(0))
Select New User(
userlists.Key,
(From scList In userlists.GroupBy(Function(s) s.Split("-"c)(0))
Select New Team(scList.Key.Split("_"c)(1), scList.OrderBy(Function(s) s).ToList())).ToList()
)
).ToList()
q3.dump()
End Sub
' Define other methods and classes here
Public class User
Public Property Name() As String
Public Property Teams() As List(Of Team)
Public Sub New(d As String, results As List(Of Team))
Me.Name = d
Me.Teams = results
End Sub
End Class
Public Class Team
Public Property TeamName() As String
Public Property FullScore() As List(Of String)
Public Sub New(name As String, value As List(Of String))
Me.TeamName = name
Me.FullScore = value
End Sub
End Class

Duplicate Settings In Reference DLL

I have a DLL with several properties and a function that generates runs a SSRS report in the background and saves it to a PDF file.
I have a DataTable with all the reports that need to be generated and where they need to be saved.
I want to make each instance of the DLL run in a separate thread. I took a stab at it found the 2nd row in the DataTable overrode the first row.
Here is the Class/DLL Code
Public Class SSRSFunctions
Private Shared _Formated_Parameters As String
Private Shared _Report_Parameters As Dictionary(Of String, String)
Public Property FORMATED_PARAMETERS() As String
Get
Return _Formated_Parameters
End Get
Set(ByVal value As String)
_Formated_Parameters = value
End Set
End Property
Public Sub New()
_Report_Parameters = New Dictionary(Of String, String)
End Sub
Public Function RenderReportToFile() As String
'RenderReportHere
End Function
Public Sub AddParameter(ByVal Name As String, ByVal Value As String)
If _Report_Parameters.ContainsKey(Name) Then
_Report_Parameters.Remove(Name)
_Report_Parameters.Add(Name, Value)
Else
_Report_Parameters.Add(Name, Value)
End If
End Sub
End Class
Here is the calling Code
Private Sub CheckForNewRequests()
'Filter DataTable for Reports to Run
For Each dr As DataRow in DateTable.Rows
Dim rpt As New SSRSFunctions
Dim t1 As New Threading.Thread(AddressOf StartNewThread)
rpt.FORMATED_PARAMETERS = (dr("REPORT_PARAMS"))
t1.Start(rpt)
Next
End Sub
Private Function StartNewThread(ByVal report As SSRSFunctions) As String
Return report.RenderReportToFile()
End Function
I am trying to figure out why the "Dim rpt As New SSRSFunctions" is not creating a new instance of the DLL and so the second row of the dataTable has a new instance to store it's parameters.
The second row is overriding the first.
Help?
Thanks
jlimited
Dont make the private properties shared, remove the Shared keyword from the declarations.
change
Private Shared _Formated_Parameters As String
Private Shared _Report_Parameters As Dictionary(Of String, String)
to
Private _Formated_Parameters As String
Private _Report_Parameters As Dictionary(Of String, String)
By sharing them you are saying that no matter how many instances of the class is created always use (share) the same instance of the shared internal variable.

Data Access Layer returns DataTable

Please have a look at the following question, which i asked some time ago: Breaking BLL (Business Logic Layer) to BLL and DAL (Data Access Layer)
This approach (Data Transfer Object) seems to work well if I am returning one record from the data access layer i.e. getNameByID returns one record.
What happens if you have a Data Access Layer function called getName(), which returns many records e.g. thousands or millions to be processed in the Business Logic Layer? (it is a scheduled task). When this is required I am currently returning a DataTable (because data readers cannot outlive a connection in VB.NET 2008). However, this question and answer seems to negate this approach: Is returning DataTable or DataSet from DAL is wrong approach. Is this a poor approach?
I realise there are ORM tools like NHibernate, which I plan to use more for future projects. However, the data access code in my current project is already written by someone else but I want to refactor it as I go along.
Update
Here is some code (as suggested by Stephen Doggart):
Imports Microsoft.VisualBasic
Public Class PersonBLL
Private Name As String
Private Age As Integer
Dim objPersonDAL As New PersonDAL
Dim personList As List(Of Person)
'Option 2
Public Function getPersonByID() As List(Of Person)
personList = objPersonDAL.getPersonByID()
Return personList
End Function
Public Function ShowMessageBox(ByVal listPersonBLL As List(Of Person))
For Each p As Person In listPersonBLL
Me.Age = p.Age
Me.Name = p.Name
MsgBox(Me.Age)
MsgBox(Me.Name)
Next
End Function
End Class
Public Class PersonDAL
Private Name As String
Private Age As Integer
Public Function getPersonByID() As List(Of Person)
'Connect to database and get Person. Return a person object
Dim personList As List(Of Person) = New List(Of Person)
Dim p1 As New Person
p1.Name = "Ian"
p1.Age = 30
personList.Add(p1)
Dim p2 As New Person
p2.Name = "Steven"
p2.Age = 28
personList.Add(p2)
Dim p3 As New Person
p3.Name = "Sharon"
p3.Age = 29
personList.Add(p3)
Return (personList)
End Function
End Class
Public Class Person
Private _Name As String
Private _Age As Integer
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Public Property Age() As Integer
Get
Return _Age
End Get
Set(ByVal value As Integer)
_Age = value
End Set
End Property
End Class
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'If Environment.GetCommandLineArgs(0) = "Test" Then
'MsgBox("Test")
'End If
Dim p1 As PersonBLL = New PersonBLL
Dim p2 As List(Of Person) = p1.getPersonByID()
Dim p3 As PersonBLL = New PersonBLL
p3.ShowMessageBox(p2)
End Sub
End Class
Returning a DataTable isn't completely terrible--there's certainly worse ways of doing it--it's only partly terrible. But, who wants to eat partly terrible food unless they have no other option?
So, unless there's some reason why you need to use a DataTable, I would recommend sticking with custom DTO classes and just have your DAL return a list of those objects. For instance:
Public Function GetNames() As List(Of NameDto)
'...
End Function