Lluís Franco on Geeks.ms
  • Home

[HowTo] – LINQ to SQL: Mostrando el progreso de Context.SumbitChanges()

  • By lfranco
  • Jun-23-2011
  • Sin categoría
  • 6 Comments.

Hola de nuevo,

 

En el proyecto que me ocupa actualmente he tenido que pelearme con algo curioso: Resulta que hay que realizar un proceso que lea una serie de ficheros de texto (si, si, de texto) que contienen una serie de información que debo analizar y posteriormente volcar a la base de datos. Hasta aquí ningún problema, salvo el de tener que lidiar con unos ficheros de texto cuyos formatos parecen haber sido diseñados por un loco (parece mentira que hoy en día hayan tantas organizaciones / empresas / bancos que sigan utilizando este método para intercambiar información).

SubmitChanges

El problema

Una vez desarrolladas las clases necesarias para leer y desglosar toda la información, me he encontrado con que se generan en el menor de los casos del orden de medio millón de filas a insertar en la BD.

Como os podéis imaginar el tiempo para realizar el volcado es considerable, del orden de entre 5 y 15 minutos. Así que he pensado en al menos optimizar la experiéncia del usuario, y me he marcado estos objetivos:

  1. Que el usuario pueda seguir trabajando mientras se insertan los registros
  2. Poder mostrar el progreso de la operación

Partimos de que tenemos un contexto de LINQ to SQL (por favor, no me preguntéis porque estamos usando esto en lugar de EF o NHibernate). En dicho contexto tenemos unas 495.000 filas pendientes de insertarse en cuanto hagamos la llamada a SubmitChanges(), y lo malo es que este método es síncrono y además no genera ningún evento ni permite usar callbacks ni ningún mecanismo de notificación del progreso.

Solución al primer problema (asincronía)

Es el más sencillo de los dos con diferencia. En versiones anteriores debíamos crear un thread manualmente, pero desde la aparición de la clase Task en .NET 4.0 esto se ha simplificado enormemente. Basta con crear un objeto Task que apunte al método que deseamos lanzar de forma asíncrona (mediante paralelismo).

importEngineInsertTask = new Task(() => insertPendingRows(), cancellationTokenSource.Token); 

importEngineInsertTask .Start();

Una de las ventajas respecto a los diferentes métodos de versiones anteriores es la posibilidad de pasar un token de cancelación, para por ejemplo poder cancelar la tarea si el usuario así lo decide. Tal vez sea interesante un post sobre esto más adelante, aunque es bastante sencillo.

Solución al segundo problema (mostrar progreso)

Este ha sido un poco más peliagudo, ya que en principio no hay modo de interactuar con el DataContext de LINQ to SQL para reportar el progreso de las inserciones. Para poder mostrar el progreso necesitamos saber por un lado el número total de filas a insertar y por otro el elemento actual. Para conocer el número total de filas a insertar basta con preguntarle al DataContext:

numInsertLines = context.GetChangeSet().Inserts.Count;

Pero para saber el número de insert actual no disponemos de nada. Sin embargo, el Contexto dispone de una propiedad Log de tipo TextWriter, que permite la monitorización de cada operación. Bien, a partir de esto, podemos generar una clase propia que herede de TextWriter:

class ActionTextWriter : TextWriter 

{ 

    private readonly Action<string> action;

 

    public ActionTextWriter(Action<string> action) 

    { 

        this.action = action; 

    }

 

    public override void Write(char[] buffer, int index, int count) 

    { 

        Write(new string(buffer, index, count)); 

    }

 

    public override void Write(string value) 

    { 

        action.Invoke(value); 

    }

 

    public override Encoding Encoding 

    { 

        get { return System.Text.Encoding.Default; } 

    } 

}

 

Y posteriormente usarla para llamar a un método que se encargue de lanzar un evento que será monitorizado para mostrar el progreso

private void configureSubmitInsertProgress() 

{ 

    context.Log = new ActionTextWriter(s => 

    { 

        if (s.StartsWith("INSERT INTO")) 

            ReportInsertProgress(s); 

    }); 

} 

Conclusión

El resultado es que el usuario puede realizar un proceso relativamente pesado sin tener que dejar de trabajar, ya que el proceso se ejecuta en segundo plano, y además puede ver un proceso del progrso de la operación. Ya sabemos lo impacientes que pueden ser los usuarios, verdad? 🙂

ReportProgress

Saludos,

Comments

6 Responsesso far

  1. anonymous dice:
    23 junio, 2011 a las 6:06 pm

    Hola Lluis, un articulo muy interesante, me gustaria saber como haces el progreso de la monitarizacion, viendo tu codigo creo que se realiza en el metodo ReportInsertProgress(s).

    Saludos.

    Responder
  2. rcorral dice:
    23 junio, 2011 a las 6:34 pm

    Un poco complicado usar LinqToSQL para esto no? Con un Command sobra… 🙂
    Por lo demás mola mucho la ocurrencia para mostrar el progreso!!!

    Un saludo titán.

    Responder
  3. lfranco dice:
    24 junio, 2011 a las 10:20 am

    @Sergio: Si, en el método ReportInsertProgress se lanza un evento que será interceptado por el ‘consumidor’ de la clase. Sólo hay que tener en cuenta que los eventos provienen desde un thread que no es el principal, con lo que no se puede acceder directamente a la interfaz de usuario. A ver si saco un ratito y posteo un ejemplo más completo 🙂
    @Rodri: Calla, calla… ya lo se. Yo no hubiese utilizado LINQ to SQL :-/ El tema es (además de que he simplificado un poco el ejemplo) viene impuesto en el diseño. Es un MUST DO o como decimos aquí POR COJONES 😉

    Responder
  4. jorge dice:
    24 junio, 2011 a las 12:07 pm

    Me apunto tu solución al «PC». 🙂
    Nunca sabes cuando te puedes encontrar con una de estas… pero… no digas nunca jamás.

    Muy buena entrada y muy muy bien pensado tito. 🙂

    Responder
  5. anonymous dice:
    24 junio, 2011 a las 3:52 pm

    Buenas!

    Una solución de lo más ingeniosa, enhorabuena :-))

    Un saludo.

    Responder
  6. anonymous dice:
    27 junio, 2011 a las 12:25 pm

    interesante, señor, grazie; sí, un ejemplo más completo sería clarificador. salu2grz

    Responder

Deja un comentario Cancelar respuesta

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

← Previous Post Next Post →

Tags

async Back best practices

Entradas recientes

  • Video de mi charla en la #dotNetSpain2016
  • I’m back. Miss me?
  • Office365 actualizado a 2013 para nuevas suscripciones
  • Serializar listas genéricas en aplicaciones WinRT
  • [TPL] Problemas de concurrencia

Comentarios recientes

  • Darling Chavez en Tip: Mostrar objetos relacionados en DevExpress GridControl
  • Alexander en [TPL] Problemas de concurrencia
  • cristinakity en Funciones escalares en TSQL, JOINS, CROSS APPLY, y la madre que parió al topo.
  • cristinakity en Funciones escalares en TSQL, JOINS, CROSS APPLY, y la madre que parió al topo.
  • anonymous en HowTo: Crear una pantalla de inicio (splash screen)

Archivos

  • marzo 2016
  • marzo 2013
  • octubre 2012
  • septiembre 2012
  • agosto 2012
  • febrero 2012
  • diciembre 2011
  • noviembre 2011
  • octubre 2011
  • septiembre 2011
  • agosto 2011
  • junio 2011
  • mayo 2011
  • abril 2011
  • febrero 2011
  • enero 2011
  • diciembre 2010
  • noviembre 2010
  • octubre 2010
  • agosto 2010
  • julio 2010
  • marzo 2010
  • febrero 2010
  • enero 2010
  • diciembre 2009
  • noviembre 2009
  • octubre 2009
  • septiembre 2009
  • agosto 2009
  • julio 2009
  • junio 2009
  • mayo 2009
  • abril 2009
  • marzo 2009
  • febrero 2009
  • enero 2009
  • diciembre 2008
  • noviembre 2008
  • octubre 2008
  • septiembre 2008
  • agosto 2008
  • julio 2008
  • junio 2008
  • mayo 2008
  • abril 2008
  • marzo 2008
  • febrero 2008
  • enero 2008
  • diciembre 2007
  • noviembre 2007
  • octubre 2007
  • septiembre 2007
  • agosto 2007
  • abril 2007
  • febrero 2007
  • enero 2007

Categorías

  • .NET
  • C#
  • Channel9
  • Evento
  • Personal
  • Videos

Meta

  • Acceder
  • RSS de las entradas
  • RSS de los comentarios
  • WordPress.org
About This Site

A cras tincidunt, ut tellus et. Gravida scel ipsum sed iaculis, nunc non nam. Placerat sed phase llus, purus purus elit.

Archives Widget
  • January 2010
  • December 2009
  • November 2009
  • October 2009
Categories
  • Entertainment
  • Technology
  • Sports & Recreation
  • Jobs & Lifestyle
Search
  • facebook
  • twitter
  • rss

Powered by WordPress  |  Business Directory by InkThemes.

This site uses cookies: Find out more.