counter for tumblr
El blog de Javier Torrecilla

Introducción:

En este post vamos a ver como utilizar otros tipos de datos numéricos dentro de una enumeración.

Si bien, no es un problema que nos vayamos a encontrar a menudo, pero que podamos realizarlo si lo necesitamos.

Al lio:

A la hora de definir nuestro enumerado tendremos que indicar uno de los siguientes valores:

- Byte

- SByte

- Short

- UShort

- Long

- ULong

- UInt

Ejemplo:

   1: public enum MyLongEnum  :long    
   2: {
   3:     Value = 1569874585236L
   4: }

Para que posteriormente podamos utilizar dicho valor desde nuestro código será necesario hacer una conversión:

   1: var myValue = (long)MyLongEnum.Value;

Espero que os sea de utilidad.

 

Saludos!

Publicado por Javier Torrecilla | 3 comment(s)
Archivado en: ,

Introducción

Ayer publique una entrada en la que hablaba de aplicar un estilo condicional a un control en xaml.

Después de  revisar si se puede utilizar StyleSelector o DataTemplateSelector, ambos son válidos a aplicar para un control como el ListBox, pero no por ejemplo para un TextBox o un TextBlock.

Código V2:

Dandole una pequeña vuelta de tuerca, y a pesar que me sigue pareciendo un “Bad Code Smell”, me he creado un nuevo converter:

   1: public class BooleanStyleConverter : IValueConverter
   2: {
   3:  
   4:     public object Convert(object value, Type targetType, object parameter, string language)
   5:     {
   6:         var page = new MyXamlPage();
   7:  
   8:         return ((bool)value ? page.Resources["StyleA"] as Style : page.Resources["StyleB"] as Style);
   9:     }
  10:  
  11:     public object ConvertBack(object value, Type targetType, object parameter, string language)
  12:     {
  13:         throw new NotImplementedException();
  14:     }
  15: }

En el converter se define la pagina donde se encuentran los estilos, y nuestro control, y a través del diccionario de recursos devolvemos un estilo según nuestro condicionante.

Su aplicación en  código:

1) Definir el converter en los recursos:

   1: <converters:BooleanStyleConverter x:Name="GenderConverter"/>

2) Definir nuestro control con su Binding y Converter:

   1: <TextBlock Text="{Binding Child.Name}" Style="{Binding Child.Gender,Converter={StaticResource GenderConverter}}" />

Espero que os sea de utilidad, o si veis alguna opción mejor, lo comenteis Open-mouthed smile

 

Saludos!

Publicado por Javier Torrecilla | 3 comment(s)
Archivado en: ,,,,,

Introducción

Estoy realizando una pequeña app para Windows8 con WinRT y Metro.

Pese a que no soy ningun experto en WPF, creo que condicionar el estilo a un elemento de Binding era algo trivial en WPF o Silverlight, en WinRT, (espero que de momento) no lo es.

Solución que hemos hablado por Twitter

En una conversación mantenida con @Antiocool y con @CorsarioVasco, hablabamos de definir 2 bloques de texto, uno para cada estilo (puesto que el condicional era para un booleano) y cada uno tuviera el estilo correspondiente. Y jugar con la visibilidad del segundo con respecto de la visibilidad del primero. Pese a que es un poco “BADSMELL”, de momento es la “única” solución rápida que he encontrado.

1) Partiendo de un Converter BooleanToVisibility

   1: public class BooleanToVisibilityConverter : IValueConverter 
   2:  {
   3:      public object Convert(object value, Type targetType, object parameter, string language)
   4:      {
   5:          bool val = (bool)value;
   6:          return (val ? Visibility.Visible : Visibility.Collapsed);
   7:            
   8:      }
   9:  
  10:      public object ConvertBack(object value, Type targetType, object parameter, string language)
  11:      {
  12:          var val = (Visibility)value;
  13:          return (val == Visibility.Visible);
  14:      }
  15:  }

2) Definiremos un Visibility Inverter Converter:

   1: public class VisibilityInverterConverter : IValueConverter 
   2:     {
   3:         public object Convert(object value, Type targetType, object parameter, string language)
   4:         {
   5:             return ((Visibility)value == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible);
   6:         }
   7:  
   8:         public object ConvertBack(object value, Type targetType, object parameter, string language)
   9:         {
  10:             return ((Visibility)value == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible);
  11:         }
  12:     }

3) Escribiremos el xaml necesario para utilizarlo:

3.1) Definimos los converters como recursos:

   1: <UserControl.Resources>
   2:        
   3:        <!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
   4:        <converters:BooleanToVisibilityConverter x:Name="BooleanConverter"/>
   5:        <converters:VisibilityInverterConverter x:Name="VisibilityConverter"/>
   6:    </UserControl.Resources>
   7: </UserControl.Resources>

3.2) Definimos los bindings necesarios:

   1: <TextBlock x:Name="tName" 
   2:     Text="{Binding Child.Name}" 
   3:     Visibility="{Binding Child.Gender,
   4:                 Converter={StaticResource BooleanConverter}}" 
   5:                 Grid.Row="0" Grid.ColumnSpan="3" 
   6:                 Style="{StaticResource ChildNameStyle}" d:LayoutOverrides="Margin"/>
   7: <TextBlock Text="{Binding Child.Name}" 
   8:             Visibility="{Binding ElementName=tName, 
   9:             Path=Visibility,Converter={StaticResource VisibilityConverter}}"
  10:              Grid.Row="0" Grid.ColumnSpan="3" 
  11:             Style="{StaticResource ChildNameStyleGirl}" d:LayoutOverrides="Margin"/>
  12:   
Como se puede observar cada TextBlock tiene el mismo texto asignado, y cambia el estilo.

La propiedad Visivility del segundo TextBlock está asignada a la misma propiedad del primer TextBlock aplicando el converter que hemos definido previamente.

 

Espero que os sea de utilidad y/o si conoceis otra alternativa, por favor comentarla!.

 

Saludos!!

Publicado por Javier Torrecilla | 1 comment(s)
Archivado en: ,,,,

Introducción

En este breve post vamos a ver como llevar a cabo la serialización de objetos dentro de una aplicación metro con WinRT y C#.

Código

Partiendo de una clase tipo:

 

   1: public class Employee
   2: {
   3:     public int Id { get; set; }
   4:     public string FirstName { get; set; }
   5:     public string LastName { get; set; }
   6:     public DateTime BirthDate { get; set; }
   7: }

Serializando un objeto:

   1: Employee obj;
   2: using (var fileStream = await ApplicationData.Current.RoamingFolder.OpenStreamForWriteAsync(String.Format("ci{0}{1}.xml",
   3:  obj.LastName, obj.FirstName), 
   4:                     CreationCollisionOption.ReplaceExisting))
   5:                 {
   6:                     var serializer = new XmlSerializer(typeof(ChildInfo));
   7:                     serializer.Serialize(fileStream, obj);
   8:                 }

Deserializando un objeto:

   1: Employee obj;
   2: sing(var fileStream = await ApplicationData.Current.RoamingFolder.OpenStreamForReadAsync(root))
   3:  
   4:    XmlSerializer serializer=new XmlSerializer(typeof(Employee));
   5:    obj = (Employee)serializer.Deserialize(fileStream);

Espero que os sirva.

 

Saludos!

Publicado por Javier Torrecilla | con no comments

Introducción:

En este post vamos a ver como trabajar con un modelo donde una de nuestras propiedades va a almacenar valores de tipo HTML, como por ejemplo el contenido del post de un blog:

   1: <p>Este el texto del post, con un enlace <a href='http://bing.com'>bing</a></p>

Abordando el problema:

Partiendo de un modelo sencillo:

   1: public class Post
   2: {
   3:     public Guid ID { get; set; }
   4:     public string Title { get; set; }
   5:     public string Content { get; set; }
   6: }

donde nuestra propiedad “Content” va a ser la que va almacenar el código HTML vamos a cualificarla con el atributo AllowHtml, para que en el momento de grabar nuestros post desde el explorador, permitirá evitar la validación de dicho campo sin tener que comprobar las etiquetas HTML introducidas en él.

Posteriormente para poder visualizar nuestro HTML con el formato HTML, vamos a hacer uso del método Raw del Helper HTML, por lo que el contenido de nuestra vista quedaría:

   1: @foreach (var c in Model)
   2: {
   3:     <article>
   4:         <h1>@c.Title</h1>
   5:         @Html.Raw(@c.Content)
   6:     </article> 
   7: }

 

Espero que os sea de utilidad.

Saludos!!

Publicado por Javier Torrecilla | con no comments
Archivado en: ,

Introducción

En este post vamos a ver como llevar a cabo la exportación de un objeto IEnumerable<T> a un DataTable. Pero, espera!!! Ya existe un método extensor CopyToDataTable, que parece realizar esta acción.

Todo parece que sí, pero parece demasiado bonito. Si nos fijamos en la firma del método:

   1: public static DataTable CopyToDataTable<T>(
   2:     this IEnumerable<T> source
   3: )
   4: where T : DataRow

Veremos que este método, solo lo podremos utilizar en el caso de que T sea de tipo DataRow o implemente a DataRow.

Bueno, el porque del post es debido a una preguntaba que lanzó ayer en Twitter @Zpektrum, sobre porque no podía utilizar dicho método con una consulta Linq.

Código:

Para ilustrar el ejemplo vamos a ver una query linq contra un DataTable:

   1: var query = from t in dt1.AsEnumerable()
   2:                         select new
   3:                         {
   4:                             ID = t.Field<string>("ID"),
   5:                             Nombre = t.Field<string>("Name")
   6:                         };

Como podeís apreciar estamos realizando una consulta sobre las filas de la tabla dt1, y escogiendo las columnas ID y Nombre. El resultado de esta consulta va a devolver un IEnumerable<T> donde T va a ser un tipo anónimo, por lo que dicho tipo no va a ser DataRow y no vamos a tener disponible el método CopyToDataTable.

Bien, para construir nuestro nuevo método extensor, tendremos que definir una nueva clase estática y un método estático que retorne un objeto de Tipo DataTable, y haremos que el método sea genérico.

   1: public static class IEnumerableExtensions
   2:     {
   3:         public static DataTable CopyAnonymusToDataTable<T>(this IEnumerable<T> info)
   4:         {
   5:             var type = typeof(T);
   6:             if (type.Equals(typeof(DataRow))) 
   7:                 return (info as IEnumerable<DataRow>).CopyToDataTable();
   8:             DataTable dt = new DataTable();
   9:             DataRow r;
  10:             type.GetProperties().ToList().ForEach(a=>  dt.Columns.Add(a.Name));
  11:             foreach (var c in info)
  12:             {
  13:                 r = dt.NewRow();
  14:                 c.GetType().GetProperties().ToList().ForEach(a => r[a.Name] = a.GetValue(c,null));
  15:                 dt.Rows.Add(r);
  16:             }
  17:             return dt;
  18:         }
  19:         
  20:     }

Para que el método sea un método extensor es necesario que sea estático, que este contenido en una clase estática y que su primer parámetro tenga el modificador “this” y el tipo que va a extender.

Explicando el método:

El método es sencillo, en primer lugar comprobamos que si el tipo de T es DataRow que ejecute el método CopyToDataTable ya existente.

En caso contrario, a través de reflectión obtendremos las propiedades del Tipo T, e iremos definiendo nuevas columnas con el nombre de dichas propiedades en la tabla resultante.

Por último, iteraremos sobre el contenido de la colección e iremos agregando las filas para devolverlos.

Ejecución del método:

Continuando con el ejemplo anterior:

   1: var query = (from t in dt1.AsEnumerable()
   2:                         select new
   3:                         {
   4:                             ID = t.Field<string>("ID"),
   5:                             Nombre = t.Field<string>("Name")
   6:                         }).CopyAnonymusToDataTable();

De está manera query, será de tipo DataTable y ya podremos manejar dicha tabla a nuestro gusto.

 

Un saludo y espero que os sea de utilidad.

Publicado por Javier Torrecilla | con no comments
Archivado en: ,

Introducción

Es bastante probable que en alguna de las tablas de vuestras bases de datos tengas alguna propiedad de tipo Integer para representar una clave ajena de una tabla que no suele cambiar.

¿Por qué no utilizar una enumeración en lugar de otra tabla en la Base de Datos?

Bien, con Entity Framework 5.0 Beta 1 podemos hacer uso de enumeraciones en nuestras entidades sin tener que hacer ninguna chapuza. Esta versión es para VS11 con .NET 4.5. El trabajo con Enums ya se pudo probar con una CTP que salio en Junio de 2011 con Database First y Model First, pero no con Code First.

En este post vamos a ver como realizarlo en los distintos enfoques existentes.

 

Database First y Model First

Ya que Database First y Model First son dos enfoques muy similares vamos a tratarlos en común.

Para empezar:

1) Definiremos un nuevo proyecto, por ejemplo: una aplicación de consola.

2) Agregaremos nuestro Entity Data Model indicando nuestra Base de Datos y tablas.

3) Dentro del explorador del modelo de nuestro diagrama, veremos una nueva carpeta “Enum Types”. Pulsaremos con el botón secundario del ratón, y elegiremos la opción “Add Enum Type”. Configuraremos las características: Nombre, tipo de dato, y los miembros:

 

 

4) Asignación de la enumeración a una propiedad escalar: Desde la ventana de propiedades

 

Code First

Para usarlo seguiremos estos pasos:

1) Definiremos nuestra enumeración:

   1: public enum Category
   2: {
   3:     Technology,
   4:     Computer,
   5:     Games
   6: }

2) Definiremos nuestra entidad:

   1: public class Products
   2: {
   3:     public int Id {get;set;}
   4:     public string Name {get;set;}
   5:     public Category Category {get;set;}
   6: }

3) Incluiremos la colección en nuestro contexto:

   1: public class Context : DbContext
   2: {
   3:     public DbSet<Product> Products {get;set;}
   4: }

4) En el momento de la definición de la Base de Datos va a definir una columna llamada Category de tipo Integer en nuestra Base de Datos.

 

Haciendo Consultas

Como las enumeraciones no estaban soportadas en versiones anteriores para poder utilizar la enumeración teniamos que obtener el código que nos interesaba.

En esta nueva versión podremos realizar la siguiente consulta:

   1: var query = context.Products.Where(a=> a.Category == Category.Games);

 

Saludos!

Publicado por Javier Torrecilla | con no comments
Archivado en: ,,,

Mini artículo para exportar un fichero word a PDF desde código c# y VB.NET.

Al Lio

Para poder trabajar con documentos de Word en nuestro código es necesario que agreguemos la referencia “Microsoft.Office.Interop.Word.dll”.

Sin mas dilación el código:

VB.NET

   1: Dim app As New Microsoft.Office.Interop.Word.Application()
   2:  
   3:         Dim missing As Object = System.Type.Missing
   4:         Try
   5:  
   6:                 Dim document As Microsoft.Office.Interop.Word.Document = app.Documents.Open(RutaCarta)
   7:                 Dim nombre As String = String.Format("{0}\TEMP\Name.doc", Application.StartupPath)
   8:  
   9:                 app.Visible = False
  10:                 
  11:                 app.ActiveDocument.ExportAsFixedFormat(String.Format("{0}\Name.pdf", Application.StartupPath), & _
  12:                      Microsoft.Office.Interop.Word.WdExportFormat.wdExportFormatPDF, False, & _
  13:                         Microsoft.Office.Interop.Word.WdExportOptimizeFor.wdExportOptimizeForPrint, & _
  14:                     Microsoft.Office.Interop.Word.WdExportRange.wdExportAllDocument)
  15:                 document.Close()
  16:    
  17:         Catch ex As Exception
  18: 'Manejar la excepción....
  19:         Finally
  20:  
  21:             app.Quit()
  22:  
  23:         End Try

 

C#

 

   1: var app = New Microsoft.Office.Interop.Word.Application()
   2: var missing = System.Type.Missing
   3: try
   4: {
   5:     var document = app.Documents.Open(RutaCarta);
   6:     string nombre = String.Format("{0}\TEMP\Name.doc", Application.StartupPath);
   7:     app.Visible = false;
   8:                 
   9:        app.ActiveDocument.ExportAsFixedFormat(String.Format("{0}\Name.pdf", Application.StartupPath), 
  10:                      Microsoft.Office.Interop.Word.WdExportFormat.wdExportFormatPDF, False, 
  11:                         Microsoft.Office.Interop.Word.WdExportOptimizeFor.wdExportOptimizeForPrint, 
  12:                     Microsoft.Office.Interop.Word.WdExportRange.wdExportAllDocument);
  13:                 document.Close();
  14: }
  15: catch(Exception ex)
  16: {
  17: 'Manejar la excepción....
  18:  }
  19: finally
  20: {
  21:     app.Quit();
  22: }

Gracias al método ExportAsFixedFormat, podremos exportar nuestro fichero Word a formato PDF o XPS…

…PERO!!!:

Podremos encontrarnos una excepción que nos dirá algo así: “Esta funcionalidad no está instalada.”.

Para poder ejecutar de forma correcta este código, necesitamos instalar un addin de Office.

 

Espero que os sea de utilidad.

 

Saludos!!!

Publicado por Javier Torrecilla | 4 comment(s)
Archivado en: ,,,,,,

Introducción

Una de las novedades que se introdujo con la versión 4.3 de Entity Framework son las llamadas Database Migration (DbMigration), que vienen a satisfacer las necesidades de los desarrolladores a la hora de realizar cambios en la estructura de la base de datos, como por ejemplo:

- Creación o borrado de Tablas.

- Cambiar o quitar columnas.

- entre otras cosas :)

La principal problemática venía a la hora de tener nuestra aplicación instalada en varios clientes y que tengan distintas versiones de Base de Datos.

Empezando

Para poder empezar a utilizar DbMigration es necesario que al “menos” tengamos instalada la versión 4.3.1(*) de Entity Framework, si no podremos instalarla desde nuget o desde la consola de comandos:

   1: Install-Package EntityFramework 

(*) Se encuentra en fase beta la version  5.0 de Entity Framework.

 

A continuación desde la consola de comandos, tendremos que especificar la habilitación de migraciones para nuestro proyecto, para ello ejecutaremos el comando “Enable-Migrations”.

Esta acción va a realizar los siguientes procesos:

- Va a buscar una clase dentro de nuestro proyecto que implemente la clase DBContext, para utilizar dicho contexto a la hora de definir las migraciones.

- Va a crear una nueva carpeta “Migrations” en nuestro proyecto. Esta carpeta va a contener los siguientes ficheros:

  1.  
    1. Clase Configuration.cs: Esta clase va a ser una implementación de la clase base genérica DbMigrationsConfiguration con nuestro objeto contexto.
      Dentro de ella, vamos a poder definir la configuración de la migración (en el constructor) y si hay que hacer algun proceso de llenado de información (método Seed).
    2. Una Migración Inicial: va a definir una clase que implemente la clase Base DbMigration, donde tendremos el método Up con la creación de nuestras entidades, y el método Down con el borrado de las mismas. La implementación de las migraciones se realiza en dos partes, la primera es la que acabamos de ver, y la segunda es relativa a información que se va a almacenar en la Base de Datos.

- Va a crear una tabla dentro de nuestra Base de Datos  llamada __MigrationHistory que va a almacenar información relativa a las migraciones.

 

¿Cómo crear una Migración?

Bien para crear una nueva migración desde la consola de comandos ejecutaremos la siguiente instrucción:

“Add-Migration Name”

Al ejecutarlo, se va a generar una nueva clase dentro de la carpeta Migrations, con una estructura similar a la mencionada anteriormente con la migración inicial.

Dentro de dicha clase vamos a tener que implementar los métodos Up, para subir de versión, y Down para bajar de versión.

¿Qué Métodos podemos utilizar en los métodos Up y Down?

AddColumn AddForeignKey AddPrimaryKey
AlterColumn CreateIndex CreateTable<T>
DropColumn DropForeignKey DropPrimaryKey
DropIndex DropTable MoveTable
RenameColumn RenameTable SQL

Ejemplo:

   1: public partial class ProductMigration : DbMigration
   2:     {
   3:         public override void Up()
   4:         {
   5:             CreateTable("Products",
   6:                 columns => new
   7:                 {
   8:                     ProductId = columns.Int(identity: true),
   9:                     Name = columns.String(maxLength: 100),
  10:                     Description = columns.String(),
  11:                     Price = columns.Decimal(precision: 10, scale: 2)
  12:                 });
  13:         }
  14:         
  15:         public override void Down()
  16:         {
  17:             DropTable("Products");
  18:         }
  19:     }

¿Cómo actualizar la Base de Datos?

Para poder ejecutar una migración tenemos dos posibles formas:

1) Desde la consola de Comandos:

A través del comando “Update-Database”. Los parámetros del comando son:

- SourceMigration <String>
- TargetMigration <String>
- Script [<SwitchParameter>]
- Force [<SwitchParameter>]
- ProjectName <String>
- StartUpProjectName <String>
- ConfigurationTypeName <String>
- ConnectionStringName <String>
- ConnectionString <String>
- ConnectionProviderName <String>
  

2) Desde código:

Definiremos un objeto del tipo Configuration, otro del tipo DbMigrator, y ejecutaremos el método Update:

   1: var config = new Migrations.Configuration();
   2:             var migrator = new DbMigrator(config);
   3:             migrator.Update();

Esta acción va a ejecutar la actualización a la ultima versión de la Base de Datos. En el caso de desear ejecutar una migración concreta o volver a una versión determinada, ejecutaríamos de la siguiente manera:

   1: var config = new Migrations.Configuration();
   2: var migrator = new DbMigrator(config);
   3: migrator.Update("201203140720163_InitialCreate");

 

 

Espero que os resulte de utilidad.

 

Saludos!

Publicado por Javier Torrecilla | 6 comment(s)
Archivado en: ,,,,,

Buenos Tardes,

El viernes pasado iba a realizar un evento online acerca de Entity Framework, por motivos ajenos a GuseNET no pudimos realizar dicho evento de forma online.

Lo primero a todos los que estabais interesados en verlo, Lo siento!!

Voy a realizarlo como tenia previsto este Viernes 09 de Marzo a las 16:30 hora de Madrid.

El enlace de registro:https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032508198&Culture=es-ES

Un saludo

Publicado por Javier Torrecilla | con no comments
Archivado en:

Buenos días,

 

Hace un tiempo publique un Evento presencial en el que voy a hablar acerca de Entity Framework para el grupo de usuarios GUSNET.

 

En vista de que varías personas han preguntado si lo ibamos a grabar o si lo ibamos a emitir de alguna manera online, y gracias a la gente de MS, tenemos la posibilidad de emitirlo vía Live Meeting.

 

La URL de registro del evento: aquí.

 

Un saludo

Publicado por Javier Torrecilla | con no comments
Archivado en:

Buenos días,

 

Desde este post lo primero que quiero hacer es daros las gracias por asistir a la charla sobre Async y Await que tuve el placer de impartir ayer.

 

Os dejo los materiales para que los descarguéis y podáis hacer vuestras pruebas.

 

- Presentación

- Demos

 

En breve también podréis acceder a la grabación de la misma para aquellos que no pudisteis asistir.

Si tenéis cualquier cosa, no dudéis en enviarme un correo o Tweet.

Un saludo.

PD: Las demos están realizadas con VS11, pero para aquellos que queráis probarlo en VS2010 solo tendréis que descargar Async CTP V3 para VS y podréis probarlo.

Publicado por Javier Torrecilla | con no comments
Archivado en: ,,

Hoy os quiero anunciar una charla que voy a impartir para el Grupo de Usuarios GUSENET.

La Agenda del Evento es:

16:30 – 19:00:  USO Y VENTAJAS DE ENTITY FRAMEWORK 4: ASÍ COMO TRUCOS PARA ENFRENTARSE A CUESTIONES HABITUALES DEL USO DIARIO
19:00 – 20:00:  Mesa redonda para debatir reuniones futuras del grupo.

¿Dónde?

Universidad de Murcia, Facultad de Informática

Aula 1.01

 

El enlace de registro: Aquí

 

Saludos

Publicado por Javier Torrecilla | 3 comment(s)
Archivado en: ,

Continuando con mi particular cruzada de crear un Mini ORM para trabajar con ORACLE, publico una nueva versión de ORAPOCO.

Historia:

OraPoco V2.0

OraPoco Update 2

OraPoco Primer Update

OraPoco 1.0

¿Qué incluye esta nueva versión?

- Corrección de un pequeño bug.

- Mejora en el rendimiento de la obtención de resultados de la base de Datos.

- BulkInsert: Permite la inserción de datos a través de OracleBulkInsert. Es bastante más rápido que ejecutar las inserciones por separados, pero no tan rápido que utilizar BulkInsert directamente puesto que es necesario hacer una serie de conversiones.

Ejemplo:

   1: Enumerable.Range(1, 300000).AsParallel().ForAll(a =>
   2: {
   3:     datos.Add(new BL_BLOQUEO_PANTALLA
   4:     {
   5:         BLPA_FECHA = DateTime.Now.ToShortDateString(),
   6:         BLPA_HORA = DateTime.Now.ToShortTimeString(),
   7:         BLPA_PANTALLA = "",
   8:         BLPA_USUARIO = "1",
   9:         BLPA_ID = a
  10:     });
  11: });
  12: db.BulkInsert<BL_BLOQUEO_PANTALLA>(datos);

¿Qué es lo que vendrá?

Proyecto de Testing, Mejoras y más equivalencia de tipos de Oracle, Procedimientos Almacenados…

 

Como siempre os pido un poquito de feedback si lo probáis.

Enlaces de Descarga:

- GitHub.

- Nuget

 

Saludos!!!

Alex Casquete me preguntó hace un tiempo si me apetecía dar una charlita sobre el nuevo paradigma de programación asíncrona, que se va a introducir con C# 5.0 y que ya podemos probar con VS 11 y la Async CTP 11, ASYNC y AWAIT.

Mi respuesta fue que si :D

Así que se queréis ver de que va todo esto el Jueves 23 de Febrero haré este WebCast en colaboración con UoC Dot Net Club.

Enlace al registro del evento. Registro.

Saludos

Publicado por Javier Torrecilla | con no comments
Archivado en: ,,,,

Introducción

Bueno, como habréis podido notar ando creando un ORM “ligero” para trabajar contra Oracle (Serie ORAPOCO).

Bien, en la segunda actualización, tanto @Eiximenis como @Juanma, me indicaban algo que las clases POCO no debía implementar ninguna clase Base, algo que a mi tampoco me convencía haberlo agregado.

Además de ello Eduard comentaba la posibilidad de hacer configuración de los Mapeos, para evitar hacer uso de Atributos en las clases POCO y que están sean completamente POCO. El tema de los mapeos es parecido a lo que se ve en FluentNhibernate.

Por otro lado @_PedroHurtado, me comentaba que era posible hacer Sql Injection, y me propuso que modificara los parámetros String por Expression.

Bueno por estos motivos nace está nueva versión de ORAPOCO.

Cambios

Modificación de la Plantilla T4

He modificado la plantilla T4, de tal forma que va a generar objetos POCO, sin ningún tipo de atributo:

Objeto POCO:

1: public class BL_BLOQUEO_PANTALLA


2: {


3: public System.Int32 BLPA_ID {get;set;}


4: public System.String BLPA_PANTALLA {get;set;}


5: public System.String BLPA_FECHA {get;set;}


6: public System.String BLPA_HORA {get;set;}


7: public System.String BLPA_USUARIO {get;set;}


8: }

Del mismo modo se van a generar unas nuevas clases, que serán los Mapeadores por defecto:

1: public class BL_BLOQUEO_PANTALLAMapper : GenericMapper<BL_BLOQUEO_PANTALLA>


2: {


3: public Type GetMappedType()


4: {


5: return typeof(BL_BLOQUEO_PANTALLA);


6: }


7: public BL_BLOQUEO_PANTALLAMapper() : base()


8: {


9: AddColumnMapping(x=> x.BLPA_ID);


10: SetPrimaryKeyField(x => x.BLPA_ID);


11: AddColumnMapping(x=> x.BLPA_PANTALLA);


12: SetNullable(x=> x.BLPA_PANTALLA);


13: AddColumnMapping(x=> x.BLPA_FECHA);


14: SetNullable(x=> x.BLPA_FECHA);


15: AddColumnMapping(x=> x.BLPA_HORA);


16: SetNullable(x=> x.BLPA_HORA);


17: AddColumnMapping(x=> x.BLPA_USUARIO);


18: SetNullable(x=> x.BLPA_USUARIO);


19: }


20: }

Creación de los Objetos OracleColumnInfo,Mapper, IGenericMapper<T> y GenericMapper<T>

Para facilitar el mapeo de datos, he creado las siguientes clases e interfaz:

Clase OraceColumnInfo para representar la información relativa a cada columna de los objetos POCO.

1: public class OracleColumnInfo


2: {


3: public OracleColumnInfo()


4: {


5: IsPk = false;


6: Nullable = false;


7: }


8: public bool IsPk { get; set; }


9: public string ColumnName { get; set; }


10: public bool Nullable { get; set; }


11: public string DefaultValue { get; set; }


12: public string SequenceName { get; set; }


13: }

La Interfaz IGenericMapper<T>:

1: public interface IGenericMapper<T>


2: {


3: IGenericMapper<T> SetPrimaryKeyField(Expression<Func<T, object>> property);


4: IGenericMapper<T> AddColumnMapping(Expression<Func<T, object>> property);


5: IGenericMapper<T> SetNullable(Expression<Func<T, object>> property);


6: IGenericMapper<T> SetDefaultValue(Expression<Func<T, object>> property, string DefaultValue);


7: IGenericMapper<T> SetSequence(Expression<Func<T, object>> property, string SequenceName);


8: }

La clase Mapper: Va a servir para representar el conjunto de columnas de un objeto POCO.

La clase: GenericMapper<T>, va a extender a Mapper e implementar a IGenericMapper:

1: public class GenericMapper<T> : Mapper,IGenericMapper<T>


2: {


3: public GenericMapper()


4: {


5: Columns = new List<OracleColumnInfo>();


6: }


7: 


8: public IGenericMapper<T> AddColumnMapping(Expression<Func<T, object>> property)


9: {


10: PropertyInfo propertyInfo = GetProperty(property);


11: return this;


12: }


13:


14: public IGenericMapper<T> SetPrimaryKeyField(Expression<Func<T, object>> property)


15: {


16: PropertyInfo propertyInfo = GetProperty(property);


17: AddColumn(propertyInfo);


18: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().IsPk = true;


19: return this;


20: }


21:


22: public IGenericMapper<T> SetNullable(Expression<Func<T, object>> property)


23: {


24: PropertyInfo propertyInfo = GetProperty(property);


25: AddColumn(propertyInfo);


26: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().Nullable = true;


27: return this;


28: }


29: public IGenericMapper<T> SetDefaultValue(Expression<Func<T, object>> property, string DefaultValue)


30: {


31: PropertyInfo propertyInfo = GetProperty(property);


32: AddColumn(propertyInfo);


33: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().DefaultValue = DefaultValue;


34: return this;


35: }


36: 


37: private void AddColumn(PropertyInfo propertyInfo)


38: {


39: var column = Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault();


40: if (column == null)


41: Columns.Add(new OracleColumnInfo { ColumnName = propertyInfo.Name });


42: }


43: private static PropertyInfo GetProperty(Expression<Func<T, object>> property)


44: {


45: PropertyInfo propertyInfo = null;


46: if (property.Body is MemberExpression)


47: {


48: propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;


49: }


50: else


51: {


52: propertyInfo = (((UnaryExpression)property.Body).Operand as MemberExpression).Member as PropertyInfo;


53: }


54: 


55: return propertyInfo;


56: }


57:


58: public IGenericMapper<T> SetSequence(Expression<Func<T, object>> property, string SequenceName)


59: {


60: PropertyInfo propertyInfo = GetProperty(property);


61: AddColumn(propertyInfo);


62: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().SequenceName = SequenceName;


63: return this;


64: }


65:


66: }

Creación de ConfigureMapping y AutoConfigureMapping

Ambos métodos van  a servir para configurar los Mapeos, y serán invocados de forma Manual.

AutoConfigureMapping, va a registrar todas aquellas clases que Implementen AutoConfigureMapping dentro del Assembly en el que nos encontremos.

Código:

Primer Ejemplo: Como crear y registrar un nuevo Mapeo:

1: db.ConfigureMapping<SAMPLE>(new GenericMapper<SAMPLE>().


2: SetPrimaryKeyField(x => x.ID).


3: AddColumnMapping(x => x.NOMBRE).


4: SetDefaultValue(x => x.ID, "SysGuid()"));

El Mapeo permite llevar a cabo:

- Creación de columnas.

- Establecer PK Simple o Compuesta.

- Establecer Valor Por Defecto.

- Establecer Nulos.

- Establecer Secuencias.

A la hora de realizar una inserción, actualización o borrado, se va a comprobar si existe un mapeo Configurado, en caso de no existir se intentará ejecutar como si tuviese Atributos.

Nuevas formas de Ejecutar Consultas:

Al haber sustituido string por Expression, se ejecutarán las consultas del siguiente modo:

1: var data = db.Query<EMPRESA>(columns:


2: (x) => new { x.EMPR_NOMBRE_CORTO, x.EMPR_NOMBRE_EMPRESA },


3: where:


4: (x => ( x.EMPR_NOMBRE_EMPRESA.Contains("D") ||


5: x.EMPR_CODIGO_EMPRESA != 101 ||


6: x.EMPR_AÑO >= 2008 ) && (


7: x.EMPR_MES == 1 )));

Los valores esperados para los parámetros “Columns”, “Where” y “Order”, van a ser expresiones lambda.

 

Finally

Bien como siempre tenéis las descargas en:

- GitHub

- Nuget

 

Y como siempre cualquier feedback será bien recibido.

Publicado por Javier Torrecilla | 5 comment(s)
Archivado en: ,,,,,

Introducción

Hace unos días publique ORAPOCO, un ORM ligero para trabajar con Bases de Datos Oracle, y su primera actualización

Cambios y Mejoras

- He realizado un poco de refactoring en el código.

- He creado una clase abstracta “Base” que va a servir como Base de nuestras Entidades.

- He añadido una propiedad para indicar el modo de ejecución de los comandos: Normal(Valor por defecto) o UnitOfWork.

- He modificado la plantilla, para dar la posibilidad de utilizar el atributo “DefaultValue” con los valores por defecto de la Base de Datos.

Ejemplos:

Las nuevas entidades generadas por la plantilla tendrán un aspecto similar a:

 

   1: public class SAMPLE : Base
   2:    {
   3:        [IsPK()]
   4:        [DefaultValue("SYS_GUID()")]
   5:        public System.Guid ID { get; set; }
   6:  
   7:  
   8:        public System.String NOMBRE { get; set; }
   9:  
  10:    }

 

Un ejemplo de utilización de UoW:

   1: var db = new POCO.Ora.TP.OracleDB("MyDB",POCO.Ora.TP.Enums.Mode.UnitOfWork);
   2: var sample = new POCO.Ora.TP.SAMPLE { NOMBRE = "Javier" };
   3: db.Insert<POCO.Ora.TP.SAMPLE>(sample);
   4: db.Insert<POCO.Ora.TP.SAMPLE>(new POCO.Ora.TP.SAMPLE { NOMBRE = "Alvaro" });
   5: db.Save();

 

Descargas:

 

Como siempre tenéis las descargas en:

- GitHub

- Nuget

Y proximamente en CodePlex.

 

Saludos, y como siempre si me dais FeedBack Genial!

Publicado por Javier Torrecilla | 6 comment(s)
Archivado en: ,,,,,

Introducción

Hace unos días publique ORAPOCO, y hoy lanzo una nueva versión con algunas features y modificaciones.

Lo nuevo

Modificaciones:

- Modificación en el atributo IsPK: Tal como comentaba Eduard en el post anterior realmente no es necesario indicar True en dicho atributo puesto que solo lo van a tener los campos que formen la PK.

- Refactorización de los métodos de Inserción, Actualización y Borrado.

Mejoras:

- Creación del atributo Sequence:

   1: internal class SequenceAttribute : System.Attribute 
   2: {
   3:     public SequenceAttribute(string sequenceName)
   4:     {
   5:         if (string.IsNullOrWhiteSpace(sequenceName)) throw new Exception("Sequence name is necessary.");
   6:         SequenceName = sequenceName;
   7:     }
   8:  
   9:     public string SequenceName { get; set; }
  10: }

 

Este atributo, que se agregará de forma manual a nuestras entidades nos va a permitir simular el uso de auto numéricos en Oracle a través de una Secuencia.

Ejemplo de uso:

   1: public class TM_EMPRESAS
   2: {
   3:     [IsPK()]
   4:     [Sequence("S_Empresas")]
   5:     public System.Int16 EMPR_CODIGO_EMPRESA {get;set;}       
   6:     public System.String EMPR_NOMBRE_EMPRESA {get;set;}       
   7: ///...
   8: }

De esta manera a la hora de ejecutar la sentencia utilizará el siguiente valor auto numérico de la secuencia indicada.

Descargas:

- GitHub

- Nuget

 

Saludos!

Publicado por Javier Torrecilla | 2 comment(s)
Archivado en: ,,,,,

Introducción

El objeto de este post, es hablar de un pequeño proyecto que acabo de publicar: ORAPOCO. Podéis encontrarlo tanto en GITHUB como en Nuget.

¿Qué es ORAPOCO?

Es un pequeño proyecto que va a permitir trabajar con objetos POCO y contra nuestra Base de Datos Oracle.

El proyecto consta de los siguientes ficheros:

- OracleDB.cs:

Esta clase va a ser la encargada de llevar a cabo todas las acciones contra la Base de Datos.

Los métodos disponibles son:

Método Descripción
QueryAll<T> Dado un tipo T(un objeto poco) va a ejecutar una consulta sobre la BD y va a devolver una lista de dicho T.
Query<T> Del mismo modo que QueryAll, va a devolver un conjunto de resultados de T, pero con la opción de seleccionar columnas, ordenar, establecer una clausula where, hacer take y skip.
Insert<T> Dado un tipo T y una entidad de dicho tipo va a tratar de realizar una inserción en la Base de Datos.
Update<T> Dado un tipo T y una entidad de dicho tipo va a tratar de realizar una actualizacion en la Base de Datos según los valores de clave primaria de dicha entidad.
Delete<T> Dado un tipo T y una entidad de dicho tipo va a tratar de realizar un borrado en la Base de Datos según los valores de clave primaria de dicha entidad.
MultipleQuery<T1,T2> Va a devolver un objeto de Tipo Tuple con el conjunto de resultados indicados por T1, T2 y T3 (2 sobrecargas)

 

- ORAPOCO.tt: Plantilla de generación de código que a través de una conexión contra la BD ORACLE va a definir clases POCO que podremos utilizar en nuestra aplicación.

Dentro de dicha plantilla se hace uso de la primera conexión existente en el fichero de configuración de la aplicación y se define una variable “_schemma” que será la que necesitemos indicar el propietario para buscar las  tablas en Oracle.

- IsPkAttribute.cs:

Atributo que va a servir para configurar las claves primarias de nuestros Objetos POCO, y que se va a utilizar a la hora de llevar a cabo las inserciones o actualizaciones, en la Base de Datos.

¿Cómo empezar a trabajar con ORAPOCO?

Podeís bajar el código fuente desde GITHUB, o instalar el paquete desde Nuget:

PM> Install-Package ORAPOCO

Ejemplos:

Un ejemplo de objeto POCO generado a través de la plantilla:

   1: public class TA_USUARIOS
   2:    {
   3:        [IsPK(true)]
   4:        public System.Int32 USUA_CODIGO_USUARIO { get; set; }
   5:  
   6:        public System.String USUA_NOMBRE { get; set; }
   7:  
   8:        public System.String USUA_APELLIDO1 { get; set; }
   9:  
  10:        public System.String USUA_APELLIDO2 { get; set; }
  11:  
  12:        public System.DateTime? USUA_FECHA_ALTA { get; set; }
  13:  
  14:        public System.String USUA_LOGIN_USUARIO { get; set; }
  15:  
  16:    }

¿Cómo  llevar a cabo una inserción?

   1: var db = new POCO.Ora.TP.OracleDB("SIFCO");
   2:  
   3:   var usu = new POCO.Ora.TP.TA_USUARIOS { 
   4:                               USUA_APELLIDO1 = "Torrecilla",
   5:                               USUA_APELLIDO2 = "Puertas", 
   6:                               USUA_CODIGO_USUARIO = 874,
   7:                               USUA_FECHA_ALTA = DateTime.Now, 
   8:                               USUA_LOGIN_USUARIO = "test", 
   9:                               USUA_NOMBRE = "Javi" };
  10:   db.Insert<POCO.Ora.TP.TA_USUARIOS>(usu);

¿Cómo llevar a cabo una actualización?

   1: var usu = new POCO.Ora.TP.TA_USUARIOS { 
   2:                             USUA_APELLIDO1 = "Torrecilla",
   3:                             USUA_APELLIDO2 = "Puertas", 
   4:                             USUA_CODIGO_USUARIO = 874,
   5:                             USUA_FECHA_ALTA = DateTime.Now, 
   6:                             USUA_LOGIN_USUARIO = "test", 
   7:                             USUA_NOMBRE = "Javi" };
   8: db.Update<POCO.Ora.TP.TA_USUARIOS>(usu);

 

¿Cómo eliminar un registro?

   1: var usu = new POCO.Ora.TP.TA_USUARIOS { 
   2:                             USUA_APELLIDO1 = "Torrecilla",
   3:                             USUA_APELLIDO2 = "Puertas", 
   4:                             USUA_CODIGO_USUARIO = 874,
   5:                             USUA_FECHA_ALTA = DateTime.Now, 
   6:                             USUA_LOGIN_USUARIO = "test", 
   7:                             USUA_NOMBRE = "Javi" };
   8: db.Delete<POCO.Ora.TP.TA_USUARIOS>(usu);

 

¿Cómo consultar datos?

- Todos los resultados:

   1: var query = db.QueryAll<POCO.Ora.TP.TA_USUARIOS>();

 

- Tomar 5 elementos y saltar 5:

   1: var query = db.Query<POCO.Ora.TP.TA_USUARIOS>(take: 5);

 

   1: var query = db.Query<POCO.Ora.TP.TA_USUARIOS>(skip: 5);

 

 

- Resultados Ordenando:

   1: var query = db.Query<POCO.Ora.TP.TA_USUARIOS>(order: "USUA_APELLIDO1 ASC, USUA_APELLIDO2 ASC");

-Resultados filtrando:

   1: var query = db.Query<POCO.Ora.TP.TA_USUARIOS>(where: "USUA_APELLIDO1 LIKE (:0)",args: new object[]{"%or%"});

- Multiples resultados:

   1: var multiQuery = db.MultipleQuery<POCO.Ora.TP.TA_USUARIOS, POCO.Ora.TP.TA_FUNCIONALIDADES>();

multiQuery tendrá dentro de “item1” la colección de Usuarios, y dentro de “item2” la colección de Funcionalidades.

 

 

Bueno espero que me dejéis comentarios, lo probéis, me digáis posibles mejoras, es decir, quiero feedback :p.

 

Saludos!

Publicado por Javier Torrecilla | 5 comment(s)
Archivado en: ,,,,,

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.

Más artículos Página siguiente >