Comparativa ORM-Lite, EF y ADO.NET - El blog de Javier Torrecilla

Comparativa ORM-Lite, EF y ADO.NET

Introducción

A raíz de la serie de post que estoy escribiendo acerca de ORMS ligeros con (Dapper, Massive y PetaPoco), y a pesar de no haber podido meter en la comparativa a Massive (lo intentaré actualizar esta noche), he decidido hacer una pequeña comparativa que me ha parecido interesante.

Comparativa

Para realizar la comparativa he utilizado los siguientes fragmentos de código, para realizar la inserción de 500 Registros en la misma tabla:

Entity Framework

   1: private static void UsingEF_B()
   2:        {
   3:            var watch = Stopwatch.StartNew();
   4:            var context = new Main.MYCVEntities1();
   5:            for (int i = 0; i < 500; i++)
   6:            {
   7:                context.CandidateInfo.AddObject(new Main.CandidateInfo
   8:                {
   9:                    CandidateId = Guid.NewGuid(),
  10:                    CandidateName = "Álvaro",
  11:                    CandidateLastName = "Torrecilla",
  12:                    CandidateEmail = "a@torrecilla.com",
  13:                    CandidateBirthDate = Convert.ToDateTime("30/12/2009"),
  14:                    CandidateCountry = 1,
  15:                    CandidateCity = 1
  16:                });
  17:  
  18:            }
  19:            context.SaveChanges();
  20:  
  21:            watch.Stop();
  22:            Console.WriteLine(String.Format("EF: {0}", watch.Elapsed.ToString()));
  23:        }

PetaPoco

   1: private static void UsingPetaPoco()
   2:         {
   3:             var watch = Stopwatch.StartNew();
   4:             //var db = new PetaPoco.Database(@"Data Source=.\SQLEXPRESS;Initial Catalog=MYCV;Integrated Security=True");
   5:             var db = PetaPocoDB.GetInstance();
   6:             var candidate = new PetaPoco.CandidateInfo
   7:             {
   8:                 
   9:                 CandidateName = "Álvaro",
  10:                 CandidateLastName = "Torrecilla",
  11:                 CandidateEmail = "a@torrecilla.com",
  12:                 CandidateBirthDate = Convert.ToDateTime("30/12/2009"),
  13:                 CandidateCountry = 1,
  14:                 CandidateCity = 1
  15:             };
  16:             for (int i = 0; i < 500; i++)
  17:             {
  18:                 candidate.CandidateId = Guid.NewGuid();
  19:                 db.Insert(candidate);
  20:             }
  21:             watch.Stop();
  22:             Console.WriteLine(String.Format("PetaPoco: {0}",watch.Elapsed.ToString()));
  23:             
  24:         }

 

Dapper

   1: private static void UsingDapper()
   2:         {
   3:             using (var connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=MYCV;Integrated Security=True"))
   4:             {
   5:                 var watch = Stopwatch.StartNew();
   6:                 connection.Open();
   7:  
   8:                 var lista = new List<dynamic>();
   9:                 for (int i = 0; i < 500; i++)
  10:                 {
  11:                     lista.Add(new
  12:                     {
  13:                         CandidateName = "Álvaro",
  14:                         CandidateLastName = "Torrecilla",
  15:                         CandidateEmail = "a@torrecilla.com",
  16:                         CandidateBirthDate = Convert.ToDateTime("30/12/2009"),
  17:                         CandidateCountry = 1,
  18:                         CandidateCity = 1
  19:                     });
  20:                 }
  21:  
  22:                 Dapper.SqlMapper.Execute(connection, @"INSERT INTO CANDIDATEINFO (CandidateName,CandidateLastName,
  23:                                                     CandidateEmail,CandidateBirthDate,CandidateCountry,CandidateCity)
  24:                          VALUES(@CandidateName,@CandidateLastName,@CandidateEmail,@CandidateBirthDate,@CandidateCountry,@CandidateCity)",
  25:                    lista);
  26:                 watch.Stop();
  27:                 Console.WriteLine(String.Format("Dapper: {0}", watch.Elapsed.ToString()));
  28:                 
  29:             }
  30:  
  31:         }

SQLCommand Simple

   1: private static void UsingSQL_A()
   2:         {
   3:             using (var connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=MYCV;Integrated Security=True"))
   4:             {
   5:                 var watch = Stopwatch.StartNew();
   6:                 connection.Open();
   7:                 using (var cmd = new SqlCommand(@"INSERT INTO CANDIDATEINFO (CandidateName,CandidateLastName,
   8:                                                     CandidateEmail,CandidateBirthDate,CandidateCountry,CandidateCity)
   9:                          VALUES(@CandidateName,@CandidateLastName,@CandidateEmail,@CandidateBirthDate,@CandidateCountry,@CandidateCity)", connection))
  10:                 {
  11:                     cmd.Parameters.AddWithValue("CandidateName", "Álvaro");
  12:                     cmd.Parameters.AddWithValue("CandidateLastName", "Torrecilla");
  13:                     cmd.Parameters.AddWithValue("CandidateEmail", "a@t.com");
  14:                     cmd.Parameters.AddWithValue("CandidateBirthDate", Convert.ToDateTime("30/12/2009"));
  15:                     cmd.Parameters.AddWithValue("CandidateCountry", 1);
  16:                     cmd.Parameters.AddWithValue("CandidateCity", 1);
  17:                     for (int i = 0; i < 500; i++)
  18:                     {
  19:                         cmd.ExecuteNonQuery();
  20:                     }
  21:                 }
  22:                 watch.Stop();
  23:                 Console.WriteLine(String.Format("SQL: {0}", watch.Elapsed.ToString()));
  24:                
  25:             }
  26:         }

SQLCommand con StringBuilder

   1: private static void UsingSQL_B()
   2:        {
   3:            StringBuilder builder = new StringBuilder();
   4:            using (var connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=MYCV;Integrated Security=True"))
   5:            {
   6:                var watch = Stopwatch.StartNew();
   7:                connection.Open();
   8:                builder.Append("INSERT INTO CANDIDATEINFO (CandidateName,CandidateLastName,CandidateEmail,CandidateBirthDate,CandidateCountry,CandidateCity) Values ");
   9:  
  10:                for (int i = 0; i < 500; i++)
  11:                {
  12:                    builder.Append("(@CandidateName,@CandidateLastName,@CandidateEmail,@CandidateBirthDate,@CandidateCountry,@CandidateCity),");
  13:                }
  14:                builder.Remove(builder.Length - 1, 1);
  15:                using (var cmd = new SqlCommand(builder.ToString(), connection))
  16:                {
  17:                    cmd.Parameters.AddWithValue("CandidateName", "Álvaro");
  18:                    cmd.Parameters.AddWithValue("CandidateLastName", "Torrecilla");
  19:                    cmd.Parameters.AddWithValue("CandidateEmail", "a@t.com");
  20:                    cmd.Parameters.AddWithValue("CandidateBirthDate", Convert.ToDateTime("30/12/2009"));
  21:                    cmd.Parameters.AddWithValue("CandidateCountry", 1);
  22:                    cmd.Parameters.AddWithValue("CandidateCity", 1);
  23:                   
  24:                        cmd.ExecuteNonQuery();
  25:                   
  26:                }
  27:                watch.Stop();
  28:                Console.WriteLine(String.Format("StringBuilder: {0}", watch.Elapsed.ToString()));
  29:                
  30:            }
  31:        }

Massive

   1: var
   2: candidateInfo = new App_Code.CandidateInfo();
   3: for (int i = 0; i < 500; i++)
   4: {
   5: var dato = candidateInfo.Insert(new
   6: {
   7: CandidateName =
   8: "Javier",
   9: CandidateLastName =
  10: "Torrecilla",
  11: CandidateBirthDate =
  12: DateTime.Now,
  13: CandidateCountry = 1,
  14: CandidateCity = 1,
  15: CandidateEmail =
  16: "r@yahoo.es"
  17: });
  18: }

SQLBULKCopy

   1: private static void UsingBulkCopy()
   2:         {
   3:             var watch = Stopwatch.StartNew();
   4:             using (var con = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\pruebas\;Extended Properties=""text;HDR=Yes;FMT=Delimited(';')"";"))
   5:             {
   6:                 con.Open();
   7:                 using (var reader = new OleDbCommand("Select * from fichero.txt", con).ExecuteReader())
   8:                 {
   9:                     using (var connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=MYCV;Integrated Security=True"))
  10:                     {
  11:                         connection.Open();
  12:                         using (var bulk = new SqlBulkCopy(connection))
  13:                         {
  14:                             bulk.DestinationTableName =
  15:                             "CandidateInfo";
  16:                             bulk.ColumnMappings.Add(0,
  17:                             "CandidateName");
  18:                             bulk.ColumnMappings.Add(1,
  19:                             "CandidateLastName");
  20:                             bulk.ColumnMappings.Add(2, "CandidateBirthDate");
  21:                             bulk.ColumnMappings.Add(3,
  22:                             "CandidateCountry");
  23:                             bulk.ColumnMappings.Add(4,
  24:                             "CandidateCity");
  25:                             bulk.ColumnMappings.Add(5,
  26:                             "CandidateEmail");
  27:                             bulk.WriteToServer(reader);
  28:                             watch.Stop();
  29:                             Console.WriteLine(String.Format("BulkCopy: {0}", watch.Elapsed.ToString()));
  30:                         }
  31:                     }
  32:                 }
  33:  
  34:             }
  35:         }

 

Una vez ejecutados todos los métodos he obtenido los siguientes resultados

 

Función Tiempo
Entity Framework 00:00:02.5485
PetaPoco 00:00:02.2437
Dapper 00:00:01.5729
SQL command Simple 00:00:01.4206
Sql command StringBuilder 00:00:00.0166
Massive 00:00:01:6641
SQL Bulk Copy 00:00:00:1712

 

Aunque los resultados entre ejecución y ejecución varían como es normal(, y mi entorno de pruebas no es un I7 con mucha ram … XD) son los resultados más comunes, aunque a veces EF era mas rápido que PetaPoco y Dapper y entre ellos mismos alternaban su orden…

Cuando publique el próximo artículo actualizaré los datos de la tabla incluyendo también los de Massive.

Saludos

UPDATE 1: He agregado el código para usar SQLBulkCopy y Massive.

PD: En poco tiempo publicaré otra actualización.

Published 12/1/2012 12:06 por Javier Torrecilla
Comparte este post:
http://geeks.ms/blogs/jtorrecilla/archive/2012/01/12/comparativa-orm-lite-ef-y-ado-net.aspx

Comentarios

# re: Comparativa ORM-Lite, EF y ADO.NET

El último ejemplo es un poco injusto, aunque interesante.

Realmente estas comparando 500 inserciones con la inserción de un batch de 500 registros.

Con NHibernate se puede definir un tamaño de batch para que optimize los insert de forma parecida, ¿se puede hacer también con EF o con alguno de los Micro-ORM?

Thursday, January 12, 2012 3:39 PM por Juanma

# re: Comparativa ORM-Lite, EF y ADO.NET

Seria interesante ver los tiempos de SqlBulkCopy, tambien .....

Thursday, January 12, 2012 5:51 PM por Veranete

# re: Comparativa ORM-Lite, EF y ADO.NET

@Juanma: el ultimo ejemplo aunque injusto es una posibilidad que brinda SQL Server mira este post geeks.ms/.../roar-inserciones-masivas-en-mongodb-vs-sql-server-iv.aspx

Te respondo de memoria pero creo que de momento no puedes hacer lo del Batch con EF y con ninguno de los MicroOrm podrias hacerlo...

@Veranete justo lo hablaba con @LluisFranco y me lo apunto para postearlo asique atentos a mi Twitter que posiblemente mañana o a mas tardar el lunes lo publicaré.

Saludos y Gracias por comentar a ambos :D

Thursday, January 12, 2012 6:17 PM por Javier Torrecilla

# re: Comparativa ORM-Lite, EF y ADO.NET

Hola Javier

Has sido un poco injusto con EF en esta comparativa , primero por que tu código no es el más optimo que se puede hacer con EF y segundo y mas importante no entiendo por que EF tiene que tener un indice Guid y calcularlo  y los demas es el SQL Server el que se ocupa de ello, esto incrementa el tiempo de EF notablemente. Tambien veo que con Massive eres "bueno" y le metes directamente a la fecha de hoy a la fecha de nacimiento en lugar de hacer un convert... todas esas cosas hacen ganar ms...

De toda manera agradezco el post por que se ve como se haria en todos. Espero que no te haya molestado mi comentario.

recibe un cordial saludo

Saturday, February 4, 2012 4:16 PM por YoKeSe

# re: Comparativa ORM-Lite, EF y ADO.NET

Interesante. no sé si ha habido actualizaciones al respecto.

Las opciones para acceso a datos entonces son ORM-Lite, EF, ADO.NET. Excluimos a Linq to SQL.

saludos.

Thursday, August 22, 2013 12:56 PM por Kiquenet