views:

248

answers:

2

I can't figure out how to define the object used in my LINQ grouping query to allow them to be strongly type.

I've build a grouping query that uses two complex objects as keys. The query works, but I would like to be able to declare the return object type.

I have a complex type...

Public Class Student
    Public Name As IndividualsName
    Public EnrolledSchool As School
    Public BirthHospital As BirthPlace
    Public Grade As Integer

    Public Sub New(ByVal name As IndividualsName, ByVal enrolledSchool As School, ByVal birthHospital As BirthPlace, ByVal grade As Integer)
        Me.Name = name
        Me.EnrolledSchool = enrolledSchool
        Me.BirthHospital = birthHospital
        Me.Grade = grade
    End Sub

End Class

Public Class School
    Inherits Location


    Public Sub New(ByVal name As String, ByVal city As String, ByVal state As String)
        MyBase.New(name, city, state)
    End Sub

    Public Shared Function GetMyHS() As School
        Return New School("My High School", "My City", "My State")
    End Function
    Public Shared Function GetYourHS() As School
        Return New School("Your High School", "Your City", "Your State")
    End Function
    Public Shared Function GetMyElementry() As School
        Return New School("My Elementary", "My City", "My State")
    End Function
    Public Shared Function GetMyMiddleSchool() As School
        Return New School("My Middle School", "My City", "My State")
    End Function

End Class

Public Class IndividualsName
    Public First As String
    Public Last As String

    Public Sub New(ByVal first As String, ByVal last As String)
        Me.First = first
        Me.Last = last
    End Sub

    Public Overrides Function ToString() As String
        Return First & " " & Last
    End Function
End Class

Public Class BirthPlace
    Inherits Location

    Public Sub New(ByVal name As String, ByVal city As String, ByVal state As String)
        MyBase.New(name, city, state)
    End Sub

    Public Shared Function GetMyHospital() As BirthPlace
        Return New BirthPlace("My General Hospital", "My City", "My State")
    End Function

    Public Shared Function GetYourHospital() As BirthPlace
        Return New BirthPlace("Your General Hospital", "Your City", "Your State")
    End Function
End Class

Public Class Location
    Public Name As String
    Public City As String
    Public State As String

    Public Sub New(ByVal name As String, ByVal city As String, ByVal state As String)
        Me.Name = name
        Me.City = city
        Me.State = state
    End Sub

    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        Return Me.GetHashCode = obj.GetHashCode
    End Function

    Public Overrides Function GetHashCode() As Integer
        Dim returnValue As Integer
        returnValue = Me.Name.GetHashCode() Xor Me.City.GetHashCode() Xor Me.State.GetHashCode()
        Return returnValue
    End Function

End Class

That I'm collecting in a list and grouping together based on two complex keys.

Sub Main()

    Dim students As List(Of Student) = New List(Of Student)

    students.Add(New Student(New IndividualsName("Bill", "Jones"), School.GetMyHS(), BirthPlace.GetMyHospital(), 9))
    students.Add(New Student(New IndividualsName("George", "Jamesen"), School.GetMyHS(), BirthPlace.GetMyHospital(), 11))
    students.Add(New Student(New IndividualsName("Chris", "McCartney"), School.GetYourHS(), BirthPlace.GetMyHospital(), 9))
    students.Add(New Student(New IndividualsName("Sara", "Smith"), School.GetMyMiddleSchool(), BirthPlace.GetMyHospital(), 7))
    students.Add(New Student(New IndividualsName("Josh", "Jefferies"), School.GetMyMiddleSchool(), BirthPlace.GetYourHospital(), 8))
    students.Add(New Student(New IndividualsName("Mel", "Tompson"), School.GetMyHS(), BirthPlace.GetMyHospital(), 12))
    students.Add(New Student(New IndividualsName("Jill", "Schmidt"), School.GetYourHS(), BirthPlace.GetMyHospital(), 10))
    students.Add(New Student(New IndividualsName("Beth", "Taylor"), School.GetMyElementry(), BirthPlace.GetMyHospital(), 5))
    students.Add(New Student(New IndividualsName("Mark", "Thatcher"), School.GetMyElementry(), BirthPlace.GetMyHospital(), 4))
    students.Add(New Student(New IndividualsName("Tom", "Jones"), School.GetMyHS(), BirthPlace.GetYourHospital(), 9))
    students.Add(New Student(New IndividualsName("Kevin", "Woo"), School.GetMyMiddleSchool(), BirthPlace.GetYourHospital(), 7))

    Dim groupedQuery 'As IEnumerable(Of IGrouping())
    groupedQuery = From student In students _
                   Group student By student.EnrolledSchool, student.BirthHospital Into Group _
                 Select EnrolledSchool, BirthHospital, StudentGroup = Group

    For Each groupItem In groupedQuery
        Console.WriteLine(String.Format("School: {0}, Birth Place: {1}", groupItem.EnrolledSchool.Name, groupItem.BirthHospital.Name))
        'Console.WriteLine(String.Format("Group Item is type {0}.", groupItem.GetType()))

        For Each item As Student In groupItem.StudentGroup
            Console.WriteLine(String.Format("Name: {0} - Grade: {1}", item.Name.ToString(), item.Grade.ToString()))
        Next

    Next
End Sub

Everything works, but it bothers me that when I process the data collection everything is declared anonymously. This is especially bothersome the object the creates the grouped list is in one assembly and it's being consumed in a web service and on a web site.

I would really like to be able to strongly type the IEnumberable that's being returned, but I can't figure out how to declare my IGrouping interface.

I should be able to transverse my data structure like...

    Dim groupedQuery As IEnumerable(Of IGrouping(Of IDoNotKnowHowToDeclareMyComplexKey, Student))
    groupedQuery = From student In students _
                   Group student By student.EnrolledSchool, student.BirthHospital Into Group _
                 Select EnrolledSchool, BirthHospital, StudentGroup = Group

    For Each groupItem As IGrouping(Of IDoNotKnowHowToDeclareMyComplexKey, Student) In groupedQuery
        Console.WriteLine(String.Format("School: {0}, Birth Place: {1}", groupItem.Key.Name)
        For Each item As Student In groupItem
            Console.WriteLine(String.Format("Name: {0} - Grade: {1}", item.Name.ToString(), item.Grade.ToString()))
        Next

    Next

but so far I haven't been able to figure out how to declare my IGrouping type.

+1  A: 

By definition, if something is anonymous, you can't call it by a name.

You should create a new class that represents your complex key type, and use that instead of anonymous types.

Bryan Watts
I tried creating a new class to be used as the key for the grouping, but I couldn't get the groupings to map into that type. I kept getting type conversion errors.
bill_the_loser
+1  A: 

As Bryan said, create a new class:

Public Class SomeName
    Public EnrolledSchool As School
    Public BirthHospital As BirthPlace
    Public StudentGroup As IEnumerable(Of Student)
End Class

Then use it while grouping:

Dim result As IEnumerable(Of SomeName)
result = (From student In students _
 Group student By student.EnrolledSchool, student.BirthHospital Into Group _
 Select New SomeName With {.EnrolledSchool = EnrolledSchool, .BirthHospital = BirthHospital, .StudentGroup = Group})

If you have any problems, place here your code and errors you get.

Kamarey
Perfect. That was exactly what I was looking for.
bill_the_loser