score:1

Accepted answer

Distinct doesn't know how to compare your items, so it returns all the items unfiltered. You should use the Distinct overload that implements IEqualityComparer. This would allow you to compare the ApplicationName and EventId properties to determine equality. However, doing so would mean having a real class, not an anonymous type. The documentation demonstrates how to achieve this in an easy to understand manner.

EDIT: I was able to use your sample with the IEqualityComparer and an EventInfo class. I added my own implementation of GetApplicationName to test.

    Dim results = (From x In doc.Descendants.Elements("ROW") _
       Select New EventInfo With {.ApplicationName = GetApplicationName(x.Element("Message")), _
        .EventId = x.Element("EventId")})

    Console.WriteLine("Total: {0}", results.Count)
    Console.WriteLine("Distinct Total: {0}", results.Distinct.Count)
    Console.WriteLine("Distinct (w/comparer) Total: {0}", results.Distinct(New EventInfoComparer()).Count)

This outputs:

Total: 2
Distinct Total: 2
Distinct (w/comparer) Total: 1

The rest of the code:

' EventInfo class and comparer
Private Function GetApplicationName(ByVal element As XElement)
    Return Regex.Match(element.Value, "Virtual\sPath:\s/(\w+)").Groups(1).Value
End Function

Public Class EventInfo
    Private _applicationName As String
    Public Property ApplicationName() As String
        Get
            Return _applicationName
        End Get
        Set(ByVal value As String)
            _applicationName = value
        End Set
    End Property

    Private _eventId As Integer
    Public Property EventId() As Integer
        Get
            Return _eventId
        End Get
        Set(ByVal value As Integer)
            _eventId = value
        End Set
    End Property
End Class

Public Class EventInfoComparer
    Implements IEqualityComparer(Of EventInfo)

    Public Function Equals1(ByVal x As EventInfo, ByVal y As EventInfo) As Boolean _
        Implements IEqualityComparer(Of EventInfo).Equals

        ' Check whether the compared objects reference the same data.
        If x Is y Then Return True

        ' Check whether any of the compared objects is null.
        If x Is Nothing OrElse y Is Nothing Then Return False

        ' Check whether the EventInfos' properties are equal.
        Return (x.ApplicationName = y.ApplicationName) AndAlso (x.EventId = y.EventId)
    End Function

    Public Function GetHashCode1(ByVal eventInfo As EventInfo) As Integer _
        Implements IEqualityComparer(Of EventInfo).GetHashCode

        ' Check whether the object is null.
        If eventInfo Is Nothing Then Return 0

        ' Get the hash code for the ApplicationName field if it is not null.
        Dim hashEventInfoAppName = _
            If(eventInfo.ApplicationName Is Nothing, 0, eventInfo.ApplicationName.GetHashCode())

        ' Get the hash code for the EventId field.
        Dim hashEventInfoId = eventInfo.EventId.GetHashCode()

        ' Calculate the hash code for the EventInfo.
        Return hashEventInfoAppName Xor hashEventInfoId
    End Function
End Class

score:1

I had never noticed this before, but it seems VB's anonymous types do not override Equals() and GetHashcode(). As such, Distinct() is checking reference equality. The easiest workaround is to build your own class that implements IEquatable<T>.

score:2

Ahmad's answer is correct and I upvoted it. However, I just wanted to point out the alternative VB.NET specific syntax for your LINQ to XML query.

Dim results = From x In doc...<ROW> _
   Select New EventInfo With {.ApplicationName = GetApplicationName(x.<Message>.Value, _
    .EventId = x.<EventId>.Value}

This returns the same result, but if you import an xmlns, then you'll get IntelliSense for the element names this way.

Here's an article describing how to get XML IntelliSense in VB.NET:

http://msdn.microsoft.com/en-us/library/bb531325.aspx


Related Query

More Query from same tag