
This post its a translation from my original post in spanish. I hope you enjoy it! 🙂
After learning the basics about LINQ and how it works, I had no choice but to test if, not only does it simplifies the code but also improves its efficiency.
I decided to test the reading speed of XML files with LINQ and determine which one has a better reading speed: .Net XML access library or its new technology, LINQ. To that purpose, I built an XML with 100.000 registers which simulates a table with students data and I will send some requests.
Those are the requests I’ve decided to do using both systems:
- Test 1: Extract all the students named Jorge.
- Test 2: Count how many students have passed.
- Test 3: Between the students named Jorge, how many have failed?
- Test 4: Between the students who have take more than an 8, which name its more common?
- Test 5: Extract the different names of the students and order them alfabethically.
After throwing these requests using the different methods here I have, in seconds, how many time each one has take:
| Test | XML Reader | LINQ |
| 1 | 0,301934 | 0,000036 |
| 2 | 0,589991 | 0,090168 |
| 3 | 0,390582 | 0,054944 |
| 4 | 0,432305 | 0,091441 |
| 5 | 0,281885 | 0,042886 |
It can be clearly apreciated the difference in access speed, being XML Reader between 7 and 8 times more slower than LINQ. But not enough with that, requests in LINQ have take up between 1 and 3 lines of code (and because I’ve want to divide it into different lines) while the ones made with the old method have take up between 10 and 30 lines of code.
For all this, LINQ seems to beat XML Reader in speed access as well as in code simplifycation, which reduces code errors and cost production or maintenance.
I know there are other methods for accesing an XML file using .Net (the XML library offers more options), but I have decided to use this one, maybe another would have been more efficient, but after making various tests using LINQ it has clearly convinced me as a data access technology.
Finally, I leave here the source code in case someone wants to have a look at it or maybe I’ve made a mistake and you want to warn me 😉
Private Sub TestVelocidad1()
Dim rutaArchivo As String = "C:/test/xmlAlumnos100.xml"
Dim metodoReader As System.Xml.XmlTextReader
Dim metodoLINQ As System.Xml.Linq.XDocument = System.Xml.Linq.XDocument.Load(rutaArchivo)
Dim lstAlumnos As New Collections.Generic.List(Of Alumno)
Dim lstNombres As New Collections.Generic.List(Of String)
Dim oAlumno As Alumno = Nothing
Dim notaAux As Byte = 0
Dim cantAprobados As Integer = 0
Dim cantSuspendidos As Integer = 0
Page.Trace.IsEnabled = True
'Prueba 1: Extraer todos los alumnos que se llamen Jorge.
Trace.Warn("Empieza la prueba 1")
Dim alumnos = From alum In metodoLINQ.Descendants("alumno") _
Where alum.Attribute("nombre") IsNot Nothing AndAlso alum.Attribute("nombre").Value = "Jorge" _
Select Nombre = alum.Attribute("nombre").Value, Nota = alum.Attribute("nota").Value
Trace.Warn("Fin prueba 1 con LINQ")
metodoReader = New System.Xml.XmlTextReader(rutaArchivo)
Do While metodoReader.Read
If metodoReader.NodeType = System.Xml.XmlNodeType.Element AndAlso metodoReader.Name = "alumno" AndAlso metodoReader.HasAttributes Then
oAlumno = New Alumno
While metodoReader.MoveToNextAttribute
If metodoReader.Name = "nombre" Then
oAlumno.Nombre = metodoReader.Value
If oAlumno.Nombre <> "Jorge" Then Exit While
ElseIf metodoReader.Name = "nota" Then
Byte.TryParse(metodoReader.Value, oAlumno.Nota)
End If
End While
If oAlumno.Nombre = "Jorge" Then lstAlumnos.Add(oAlumno)
End If
Loop
Trace.Warn("Fin prueba 1 con Reader")
'Prueba 2: Contar cuantos alumnos han aprobado.
Trace.Warn("Empieza la prueba 2")
cantAprobados = (From alum In metodoLINQ.Descendants("alumno") Where alum.Attribute("nota") IsNot Nothing AndAlso Integer.Parse(alum.Attribute("nota").Value) > 4).Count
Trace.Warn("Fin prueba 2 con LINQ")
metodoReader = New System.Xml.XmlTextReader(rutaArchivo)
cantAprobados = 0
Do While metodoReader.Read
If metodoReader.NodeType = System.Xml.XmlNodeType.Element AndAlso metodoReader.Name = "alumno" AndAlso metodoReader.HasAttributes Then
notaAux = 0
While metodoReader.MoveToNextAttribute
If metodoReader.Name = "nota" Then
Byte.TryParse(metodoReader.Value, notaAux)
If notaAux > 4 Then cantAprobados += 1
End If
End While
End If
Loop
Trace.Warn("Fin prueba 2 con Reader")
'Prueba 3: De entre los alumnos que se llaman Jorge, ¿cuántos han suspendido?
Trace.Warn("Empieza la prueba 3")
cantSuspendidos = (From alum In metodoLINQ.Descendants("alumno") _
Where alum.Attribute("nombre") IsNot Nothing AndAlso alum.Attribute("nombre").Value = "Jorge" _
Where alum.Attribute("nota") IsNot Nothing AndAlso Integer.Parse(alum.Attribute("nota").Value) < 5).Count
Trace.Warn("Fin prueba 3 con LINQ")
metodoReader = New System.Xml.XmlTextReader(rutaArchivo)
cantSuspendidos = 0
oAlumno = New Alumno
Do While metodoReader.Read
If metodoReader.NodeType = System.Xml.XmlNodeType.Element AndAlso metodoReader.Name = "alumno" AndAlso metodoReader.HasAttributes Then
notaAux = 0 : oAlumno.Nombre = "" : oAlumno.Nota = 0
While metodoReader.MoveToNextAttribute
If metodoReader.Name = "nota" Then Byte.TryParse(metodoReader.Value, oAlumno.Nota)
If metodoReader.Name = "nombre" Then oAlumno.Nombre = metodoReader.Value
End While
If oAlumno.Nombre = "Jorge" And oAlumno.Nota < 5 Then cantSuspendidos += 1
End If
Loop
Trace.Warn("Fin prueba 3 con Reader")
'Prueba 4: De entre los alumnos que han sacado más de un 8, ¿cual es el nombre más predominante?
Dim NombreMasEmpollon As String = ""
Trace.Warn("Empieza la prueba 4")
Dim empollon As Generic.List(Of String) = (From n In (From alum In metodoLINQ.Descendants("alumno") _
Where (alum.Attribute("nota") IsNot Nothing AndAlso Integer.Parse(alum.Attribute("nota").Value) > <img src="http://www.entrecodigos.com/wp-includes/images/smilies/icon_cool.gif" alt="8)" /> _
Group alum By nombre = alum.Attribute("nombre").Value Into cantNombres = Count() _
Select nombre, cantNombres Order By cantNombres Descending Order By cantNombres Descending Take 1) Select n.nombre).ToList
NombreMasEmpollon = empollon.Item(0).ToString
Trace.Warn("Fin prueba 4 con LINQ")
metodoReader = New System.Xml.XmlTextReader(rutaArchivo)
Dim klstNombres As New Generic.SortedList(Of String, Integer)
oAlumno = New Alumno
Do While metodoReader.Read
If metodoReader.NodeType = System.Xml.XmlNodeType.Element AndAlso metodoReader.Name = "alumno" AndAlso metodoReader.HasAttributes Then
oAlumno.Nombre = "" : oAlumno.Nota = 0
While metodoReader.MoveToNextAttribute
If metodoReader.Name = "nota" Then Byte.TryParse(metodoReader.Value, oAlumno.Nota)
If metodoReader.Name = "nombre" Then oAlumno.Nombre = metodoReader.Value
If oAlumno.Nota > 0 And oAlumno.Nombre.Length > 0 Then
If oAlumno.Nota > 8 Then
If klstNombres.Keys.IndexOf(oAlumno.Nombre) < 0 Then
klstNombres.Add(oAlumno.Nombre, 1)
Else
klstNombres.Item(oAlumno.Nombre) = klstNombres.Item(oAlumno.Nombre) + 1
End If
End If
Exit While
End If
End While
End If
Loop
NombreMasEmpollon = klstNombres.Keys(0)
For Each n As String In klstNombres.Keys
If klstNombres(n) > klstNombres(NombreMasEmpollon) Then
NombreMasEmpollon = n
End If
Next
Trace.Warn("Fin prueba 4 con Reader")
'Prueba 5: Extraer sólo los nombres distintos de los alumnos y ordenarlos.
Trace.Warn("Empieza la prueba 5")
Dim nombres As String() = (From alum In metodoLINQ.Descendants("alumno") Select n = alum.Attribute("nombre").Value Distinct Order By n.ToString).ToArray
Trace.Warn("Fin prueba 5 con LINQ")
metodoReader = New System.Xml.XmlTextReader(rutaArchivo)
lstNombres.Clear()
Do While metodoReader.Read
If metodoReader.NodeType = System.Xml.XmlNodeType.Element AndAlso metodoReader.Name = "alumno" AndAlso metodoReader.HasAttributes Then
While metodoReader.MoveToNextAttribute
If metodoReader.Name = "nombre" Then
If lstNombres.IndexOf(metodoReader.Value) < 0 Then
lstNombres.Add(metodoReader.Value)
End If
Exit While
End If
End While
End If
Loop
lstNombres.Sort()
Trace.Warn("Fin prueba 5 con Reader")
End Sub
Public Class Alumno
#Region "Variables"
Private _nombre As String
Private _nota As Byte
#End Region
#Region "Propiedades"
Public Property Nombre() As String
Get
Return _nombre
End Get
Set(ByVal value As String)
_nombre = value
End Set
End Property
Public Property Nota() As Byte
Get
Return _nota
End Get
Set(ByVal value As Byte)
_nota = value
End Set
End Property
#End Region
End Class