Data Access Layer returns DataTable - vb.net

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

Related

How to access object in multidimensional array in Visual Basic

In Visual Basic, I am using a List to store an array of objects. However, I haven't figured out how to access a particular object once I have added it to my list.
I am using Microsoft Visual Studio
Private Results As New List(Of Object)
Dim Results As Object()
Dim CaseStatus as String = "Closed"
...
Results.Add(New Object() {CaseStatus, FlagsStr, OBTotal, OBPaid})
MessageBox.Show(Result(0).ToString)
Displays: System.Object[]
I have also tried MessageBox.Show(Results(0,0).ToString), but don't get the expected results.
When you can .ToString on an object you get the fully qualified name of the object unless the class overrides .ToString. Creating a class and a List (Of T) gives you easy access to any of the properties of the class.
Public Class ProjectCase
Public Property CaseStatus As String
Public Property FlagsString As String
Public Property OBTotal As Decimal
Public Property OBPaid As Decimal
Public Sub New()
'include a default constructor
End Sub
Public Sub New(cs As String, fs As String, total As Decimal, paid As Decimal)
CaseStatus = cs
FlagsString = fs
OBTotal = total
OBPaid = paid
End Sub
Public Overrides Function ToString() As String
Return $"{CaseStatus}, {FlagsString}, Total = {OBTotal}, Paid = {OBPaid}"
End Function
End Class
Private Results As New List(Of ProjectCase)
Private Sub OPCode()
Results.Add(New ProjectCase("Closed", "Some string", 74.36D, 22D))
MessageBox.Show(Results(0).ToString)
MessageBox.Show(Results(0).CaseStatus) 'or any property of your class
End Sub

VB.NET How To Prevent Infinite Recursion During Object Population

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.

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

Entity Framework Filling IEnurable(Of Cars) --- How do I fill

Fellow Coders, How do I use the entity framework to fill a POCO? In other words I have coded out a Class Called Car with lets say 3 properties
Public Class Car
Private _car_id As Int32
Private _car_make As String
Private _car_model As String
Public Sub New(ByVal car_id As Int32, _
ByVal car_make As String, _
ByVal car_model As String)
End Sub
Public Property Car_id As Int32
Get
Return _car_id
End Get
Set(value As Int32)
_car_id = value
End Set
End Property
Public Property Car_Make As String
Get
Return _Car_Make
End Get
Set(value As String)
_car_make = value
End Set
End Property
Public Property Car_Model As String
Get
Return _car_model
End Get
Set(value As String)
_car_model = value
End Set
End Property
End Class
Now I need to populate an IEnumerable to keep a list of my Car Objects for future use and I will need to perform a cross compare on another Entity Call in my code.
Public Function GetCars() As IEnumerable(Of CarDB)
Dim data As New List(Of CarDB)
Using ctx As New FundingEntities()
Dim query = From x In ctx.tbl_cars
Select New ???????????
This is where I get lost... How do I fill my IEnumberabl(Of CarDB)????
Any good POCO and Entity Coders please lend a hand, so I can get over this hump...
Thanks
This may be more LINQ related. As long as tbl_cars contains all three fields that you intend to store in a "Car" object, you can do the following:
Dim query As IEnumerable(Of Car) = From x In ctx.tbl_cars Select New Car With {.Car_id = x.Id, .Car_make = x.Make, .Car_Model = x.Model}

Generic Collections, Member Classes, Design Pattern question for VB.NET

I have a class called Person:
Public Class Person
Private PersonID as String
Private Name as String
Private Records as GenericCollection(Of PublicRecord)
Public Sub New(ByVal ID as String)
Me.PersonID = ID
Me.Name = getPersonName(ID)
End Sub
'Get/Sets
End Class
getPersonName is simply a function that does exactly as it is described. GenericCollection class is as follows:
Public Class GenericCollection(Of ItemType)
Inherits CollectionBase
' Purpose: Provides a generic collection class from which all other collections
' classes can be inherited if they wish to extend the functionality below.
#Region "Public Methods"
Public Function Add(ByVal NewObject As ItemType) As Integer
Return MyBase.InnerList.Add(NewObject)
End Function
Public Sub New()
MyBase.New()
End Sub
#End Region
#Region "Public Properties"
Default Public Property Item(ByVal Index As Integer) As ItemType
Get
Return CType(MyBase.InnerList(Index), ItemType)
End Get
Set(ByVal value As ItemType)
MyBase.InnerList(Index) = value
End Set
End Property
#End Region
End Class
PublicRecord class is:
Public Class PublicRecord
Private RecordID As String
Private RecordDataOne As String
Private RecordDataTwo As String
Public Sub New()
MyBase.New()
End Sub
'Get/Sets
End Class
One of the requirements I've been told can be done is that I should be able to grab all Persons in a Collection of Persons, then since all of those Persons will have Collectinos of Records within them... grab a specific set of data from the Collection of Records.
We'll say, I want to: getPersonsOverAge21() from the Collection of Records inside each Person inside the Collection of Persons.
Is this even possible? If so, can someone explain how it would work?
There's no need to implement your own generic collection class. .Net has already done this for you in the System.Collections.Generic namespace. Look at a List(Of Person) or even just a simple IEnumerable(Of Person).
Now you haven't explained how your record objects relate to your person type or what data they contain, so I can only speculate on the next part. But it sounds kind of like you want something like this:
Dim people As List(Of Person) = GetPeopleFromDatabase()
Dim peopleOver21 As IEnumerable(Of Person) = people.Where(Function(p) p.Age >= 21)
Dim peopleOver21Query = From p In people _
Where (p.Age >= 21) _
Select p