Left Outer Join doesnt show empty elements - vb.net

I try to perform an left outer join within linq. My Datasource is:
Public Class Department
Public Property ID As Integer
Public Property Name As String
Public Shared Function GetAllDepartments() As List(Of Department)
Return New List(Of Department) From { _
New Department With {.ID = 1, .Name = "IT"},
New Department With {.ID = 2, .Name = "HR"},
New Department With {.ID = 3, .Name = "Payroll"}
}
End Function
End Class
Public Class Employee
Public Property ID As Integer
Public Property Name As String
Public Property DepartmentID As Integer
Public Shared Function GetAllEmployees() As List(Of Employee)
Return New List(Of Employee) From { _
New Employee With {.ID = 1, .Name = "Mark", .DepartmentID = 1},
New Employee With {.ID = 10, .Name = "Andy"}
}
End Function
End Class
My query is:
'Left Outer Join
Dim result = From emp In Employee.GetAllEmployees
Group Join dep In Department.GetAllDepartments
On emp.DepartmentID Equals dep.ID Into eGroup = Group
From all In eGroup.DefaultIfEmpty
Select New With {
.Person = emp.Name,
.DepartmentName = If(all.Name Is Nothing, "No Department", all.Name)}
What I expect to get is:
Mark IT
Andy No Department
But what I get is
Mark IT
Could anyone tell me, what is wrong with the query? I just cannot see it, even after reading msdn help for it over again, I just cannot find what is wrong.

I cannot reproduce this behavior because when I try to run your code it throws a NullReferenceException.
However, if I change this code:
.DepartmentName = If(all.Name Is Nothing, "No Department", all.Name)}
To this code:
.DepartmentName = If((all Is Nothing), "No Department", all.Name)}
I get the expected output:
Mark, ITAndy, No Department

Related

(SQL, VB.NET) How Do I select multiple values from one row and assign them to variables?

I am writing a SELECT query to use in my project. So far, I have
Dim conn As New OleDbConnection
Dim StudentID, GradeID, SubjectID As Integer
Dim YourGrade(4), YourSubject(4) As String
conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source =H:\Year 13 Computer Science\Project\Usernames and Passwords.accdb"
conn.Open()
Dim sql = "Select * From Grades where StudentID =" & CurrentID
Dim cmd As New OleDbCommand(sql, conn)
Dim dr As OleDbDataReader = cmd.ExecuteReader
While dr.Read
StudentID = dr("StudentID")
GradeID = dr("GradeID")
SubjectID = dr("SubjectID")
End While
My issue is that I need to be able to have a dynamic number of SubjectIDs and GradeIDs to be selected, in case a student is taking more or fewer subjects than the normal three.
My Query produces:
StudentID GradeID SubjectID
1 2 1
1 4 13
1 3 19
the CurrentID is "1" for the purposes of this.
Each GradeID and SubjectID corresponds to values in other tables which I can work on later.
I need to be able to have each of those three Grade IDs in a separate value, an array could be used but I don't know how to code it. I attempted it earlier as shown by the "YourGrade(4), YourSubject(4)".
I intend to use the data to fill out a Data Grid.
Create a domain object for "Student" and then load the records into a list of Student Objects.
I don't know VB.net, but the equivalent Domain object in C# would look like this:
public class Student
{
public int StudentId { get; set; }
public int GradeId { get; set; }
public int SubjectId { get; set; }
}
And then the code to loop through the dataReader and populate the list:
List<Student> results = new List<Student>();
while (dr.Read())
{
results.Add(new Student()
{
StudentId = Convert.ToInt32(dr["StudentID"]),
GradeId = Convert.ToInt32(dr["GradeId"]),
SubjectId = Convert.ToInt32(dr["SubjectId"])
});
}
-- Edit 2/2/2017 --
Turns out there are free converters on the web. These are the VB.net equivalents to the snippets above.
Class:
Public Class Student
Public Property StudentId() As Integer
Get
Return m_StudentId
End Get
Set
m_StudentId = Value
End Set
End Property
Private m_StudentId As Integer
Public Property GradeId() As Integer
Get
Return m_GradeId
End Get
Set
m_GradeId = Value
End Set
End Property
Private m_GradeId As Integer
Public Property SubjectId() As Integer
Get
Return m_SubjectId
End Get
Set
m_SubjectId = Value
End Set
End Property
Private m_SubjectId As Integer
End Class
Database Code:
Dim results As New List(Of Student)()
While dr.Read()
results.Add(New Student() With { _
Key .StudentId = Convert.ToInt32(dr("StudentID")), _
Key .GradeId = Convert.ToInt32(dr("GradeId")), _
Key .SubjectId = Convert.ToInt32(dr("SubjectId")) _
})
End While
I did some digging, found out that to fill out a datagrid in the way I was attempting to, the simplest way is to use an INNER JOIN SQL statement. In SQL the query is:
SELECT Students.FirstName, Students.LastName, Subjects.SubjectName, GradeVals.Grade
FROM GradeVals INNER JOIN (Students INNER JOIN (Subjects INNER JOIN Grades ON Subjects.SubjectID = Grades.SubjectID) ON Students.StudentID = Grades.StudentID) ON GradeVals.GradeID = Grades.GradeID;
That's using my Table names and such as.

object Collection Containing a List of IDs Linq

I have a list box and the user is able to multi-select. I want to use Linq and bring back the records of the selected IDs that the user selects. I need to bring back the full object record for each selected ID
Here is the contact object along with collection object
Namespace MODEL
<System.Serializable()> _
Public Class ContactCollection
Inherits System.Collections.ObjectModel.Collection(Of Contact)
Implements IList(Of Contact)
End Class
End Namespace
Namespace MODEL
<System.Serializable()> _
Public Class Contact
Private mContactID As Int32 = 0
Private mFirstName As String
Private mLastName As String
Public Property ContactID As Int32
Get
Return mContactID
End Get
Set(value As Int32)
mContactID = value
End Set
End Property
Public Property FirstName As String
Get
Return mFirstName
End Get
Set(value As String)
mFirstName = value
End Set
End Property
Public Property LastName As String
Get
Return mLastName
End Get
Set(value As String)
mLastName = value
End Set
End Property
End Class
End Namespace
Adding 5 Records to the collection object
Dim objCollection As New MODEL.ContactCollection
Dim obj As New MODEL.Contact
objCollection.Add(New MODEL.Contact With {
.ContactID = 1, _
.FirstName = "John", _
.LastName = "Smtih" _
})
objCollection.Add(New MODEL.Contact With {
.ContactID = 2, _
.FirstName = "Mark", _
.LastName = "Davis" _
})
objCollection.Add(New MODEL.Contact With {
.ContactID = 3, _
.FirstName = "Tom", _
.LastName = "Howe" _
})
objCollection.Add(New MODEL.Contact With {
.ContactID = 4, _
.FirstName = "Jerry", _
.LastName = "Thomas" _
})
objCollection.Add(New MODEL.Contact With {
.ContactID = 5, _
.FirstName = "Jane", _
.LastName = "Marry" _
})
This is the selected contact List from the list box
Dim lstContacts As New List(Of Integer)
lstContacts.Add(2)
lstContacts.Add(4)
I am not sure what to do at this point with Linq to find the values. I think I have to use contains but I have tried may different ways but I was unable to get the values.
I have tried this Linq but does not work or bring any records back
Dim objSearch from SearchContacts in objCollection
Where (lstContacts.Contains(SearchContacts.ContactID))
To get the Ids, try that :
Dim ids As IEnumerable(Of Int32) = myListBox.SelectedItems _
.OfType(Of Contact)() _
.Select( Function(c) c.ContactID ) _
Edit
If you want the Contacts, you can just just :
Dim ids As IEnumerable(Of Contact) = myListBox.SelectedItems _
.OfType(Of Contact)()
And if you want the contacts in a separate copied collection, you can :
Dim ids As List(Of Contact) = myListBox.SelectedItems _
.OfType(Of Contact)() _
.ToList()
Last (if think this is your real question - just tell and I erase everything above)
Dim selectedContacts As IEnumerable(Of MODEL.Contact) = From contact In objCollection
Join id In lstContacts
On contact.ContactID Equals id
Select contact

Create own data examples in LinqPad

I have a class (vb.net) with some data that I want to query in LinqPad. I already worked with some examples as the one from "Linq in Action" so they work with some kind of classes with data as well to explain queries. But I just cannot find anything about how to import or write your own classes. Could anyone help me here?
My Class looks like:
Public Class Employee
Public Property ID As Integer
Public Property Salery As Integer
Public Property Name As String
Public Property Department As String
Public Property Gender As String
Public Shared Function GetAllEmployees() As List(Of Employee)
Return New List(Of Employee) From { _
New Employee With {.ID = 1, .Name = "Mark", .Department = "HR", .Gender = "Male", .Salery = 12000},
New Employee With {.ID = 2, .Name = "Sandra", .Department = "IT", .Gender = "Female", .Salery = 2000} _
}
End Function
End Class
You might be missing a couple things about using LINQPad:
Set the Language to "VB Program" and put classes where the comment says to.
Use the Dump method to output an expression. (For "VB Expression", Dump is called automatically.)
Here is an example. (Note, you might be using that SQL-looking syntax.)
Sub Main
Employee.GetAllEmployees() _
.Where(Function (employee) employee.Department = "HR") _
.Dump()
Dim hrEmployees = From employee In Employee.GetAllEmployees()
Where employee.Department = "HR"
hrEmployees.Dump()
End Sub
' Define other methods and classes here
Public Class Employee
Public Property ID As Integer
Public Property Salery As Integer
Public Property Name As String
Public Property Department As String
Public Property Gender As String
Public Shared Function GetAllEmployees() As List(Of Employee)
Return New List(Of Employee) From { _
New Employee With {.ID = 1, .Name = "Mark", .Department = "HR", .Gender = "Male", .Salery = 12000},
New Employee With {.ID = 2, .Name = "Sandra", .Department = "IT", .Gender = "Female", .Salery = 2000} _
}
End Function
End Class

Conversion Linq result to object type in vb.net

I need vb.net syntax of below answer(given in c#)-
How can I convert Linq results to DTO class object without iteration
I have tried converting to vb.net at this link, but it does give compilation errors.
Following is the output from above convertor -
Public Function [Get]() As List(Of User)
Using db = New MyContext()
Return (From u In db.UsersOrder By u.FirstNameNew User() With { _
Key .Id = u.pkUser, _
Key .Username = u.Username, _
Key .Password = u.Password, _
Key .Active = u.Active _
}).ToList()
End Using
End Function
Well, the Linq query is obviously messed up. And since there's no User class in the code you tried to convert, the converter tried to use the Key keyword, which is only used for anonymous types.
So the correct code should look like:
Public Function [Get]() As List(Of User)
Using db = New MyContext()
Return (From u In db.Users
Order By u.FirstName
Select New User() With {
.Id = u.pkUser,
.Username = u.Username,
.Password = u.Password,
.Active = u.Active
}).ToList()
End Using
End Function
assuming the following User class:
Public Class User
Public Id As Integer
Public Username As String
Public Password As String
Public Active As Boolean
End Class
It worked now -
Following are the keyword were missing in convertor.
Order
Select New
removed keyword Key
and final code is as below -
Public Function [Get]() As List(Of User)
Using db = New MyContext()
Return (From u In db.UsersOrder Order By u.FirstNameNew select new User() With { _
.Id = u.pkUser, _
.Username = u.Username, _
.Password = u.Password, _
.Active = u.Active _
}).ToList()
End Using

Multiple Parameters in LINQ to SQL

I am trying to pass several search parameters to an LINQ expression to retrieve all entries that contain one of the search items.
Example:
Dim query = From p In db.BEW_PROFIL
For Each searchItem As String In searchItems
Dim item As String = searchItem
query = query.Where(Function(p) p.NAME = item)
Next
Problem here is I don´t get any results because the Where clause looks with that code something like this.
... Where p.NAME = item1 AND p.NAME = item2
What i need is an OR between the parameters, but I don´t get it how I can achieve this.
Any help would be greatly appreciated.
Got it...
void Main()
{
var searchItems = new string[] { "test", "past", "most", "last", "fast", "feast", "yeast", "cast" };
var query = from p in searchItems select new MyClass { Name = p };
Predicate<MyClass> whereClause = _ => false;
foreach (var item in searchItems)
{
var searchItem = item;
Predicate<MyClass> oldClause = whereClause;
whereClause = p => p.Name == searchItem || oldClause(p);
}
query = query.Where(p => whereClause(p));
query.Dump();
}
public class MyClass
{
public MyClass() { }
public string Name { get; set; }
}
The code was ran in LINQPad, and that returned every element.
Here is that code translated to Vb.Net
Private Sub Main()
Dim searchItems = New String() {"test", "past", "most", "last", "fast", "feast", "yeast", "cast"}
Dim query = From p In searchItems Select New [MyClass]() With { .Name = p }
Dim whereClause As Predicate(Of [MyClass]) = Function(element) False
For Each item As String In searchItems
Dim searchItem = item
Dim oldClause As Predicate(Of [MyClass]) = whereClause
whereClause = Function(p) p.Name = searchItem OrElse oldClause(p)
Next
query = query.Where(Function(p) whereClause(p))
query.Dump()
End Sub
Public Class [MyClass]
Public Sub New()
End Sub
Public Property Name() As String
Get
Return m_Name
End Get
Set
m_Name = Value
End Set
End Property
Private m_Name As String
End Class