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