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.

5 comentarios en “Comparativa ORM-Lite, EF y ADO.NET”

  1. 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?

  2. @Juanma: el ultimo ejemplo aunque injusto es una posibilidad que brinda SQL Server mira este post http://geeks.ms/blogs/palvarez/archive/2011/11/22/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 😀

  3. 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

  4. 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.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *