Archivo

Posts Tagged ‘c#’

Métodos asíncronos en base a eventos (C#)

A continuación, como crear un formulario que llame a métodos de  clases de forma asíncrona. De tal forma que la interfaz no se quede “tostada” cuando estemos haciendo operaciones pesadas.

Nota 1: Lo he intentado reducir a la mínima expresión para quede claro el concepto en sí. He cogido y modificado a mi conveniencia un ejemplo que tiene disponible Microsoft en la MSDN, así que si lo queréis más completo, ya sabéis.

Qué voy a hacer y qué no:

El ejemplo consiste en un formulario que obtiene datos de una BBDD y los escribe en un datagrid. No voy a poner el código de como se conecta a una BBDD ni como se rellena un datagrid. Mi intención es dejar un esquema con lo más básico sobre el manejo de los componentes asíncronos.

Empezamos:

Clase que contiene los métodos asíncronos:

namespace Ejemplo.DBOperations
{
    //#1 Delegado del evento que notifica cuando termina la operación.
    public delegate void GetDataCompletedEventHandler(object sender, GetDataCompletedEventArgs e);
        
    public class GetDBDataModule
    {
		//#2 Delegado por el cual vamos a llamar asíncronamente al método que va a realizar el trabajo.
        private delegate void SearchWorkerEventHandler(string criteria);
       
        #region Events
		
		//#3 Evento que se lanza cuando se termina la operación
        public event GetDataCompletedEventHandler GetDataCompleted;

        #endregion

        #region MEMBERS and PROPERTIES

        private DataAccessLayer _DAL;         
		public DataAccessLayer DAL{get;set;}
            
        #endregion

        #region Public METHODS
		
		//#4 Metodo sincrono. 
        public DataSet GetData(string criteria)
        {
            DataSet qResult = null;
            qResult DAL.GetDataFromDB(criteria);
            return qResult;
        }
		
		//#5 Metodo asincrono.
        public virtual void GetDataAsync(String criteria)
        {
			//#6 Nos creamos un delegado que apunta a la función que va a hacer el trabajo y lo llamamos asincronamente.

            SearchWorkerEventHandler SearchDelegate = 
                new SearchWorkerEventHandler(SearchWorker);
            SearchDelegate.BeginInvoke(criteria, null, null);
        }

        #endregion

        #region PRIVATE METHODS
        
		//#7 Este es el metodo que realiza todo el trabajo asíncrono.
		//Desde este metodo se llama al metodo sincrono.
        private void SearchWorker(string criteria)
        {
            Exception myException = null;
            DataSet ds = null;
           
            try
            {
                ds = GetData(criteria);
            }
            catch (Exception ex)
            {
                myException = ex;
            }
			//#8 Una vez terminado, lanzamos el evento, inicializando los argumentos necesarios.
            GetDataCompletedEventArgs e = new GetDataCompletedEventArgs(ds, myException);
            OnGetDataCompleted(e);

        }

        #endregion
		//#9 Para lanzar de forma segura el evento.
        protected void OnGetDataCompleted(GetDataCompletedEventArgs e)
        {
            if (GetDataCompleted != null)
            {
                GetDataCompleted(this, e);
            }
        }

    }

Creamos también una clase con los argumentos que vamos a pasar a traves del evento. En este caso, solo pasamos una (posible) excepción y el resultado, que es un Dataset. En el artículo original, los argumentos heredan de la clase AsyncCompletedEventArgs. En mi caso, como he simplificado lo máximo posible simplemente no me hace falta y no lo uso.

//#10 Argumentos que se van a devolver cuando se lance el evento.
public class GetDataCompletedEventArgs
    {
		#region MEMBERS and PROPERTIES
        DataSet qresultValue = null;
		Exception exceptionValue;
		
		public Exception exception{get;private set;}
        public DataSet queryResults{get;}
        #endregion
		
        public GetDataCompletedEventArgs(
            DataSet qresult,
            Exception e)
        {
            this.qresultValue = qresult;
            this.exceptionValue = e;
        }    
    }

Formulario:

En el punto 1 y 2 se puede ver como se hace uso de un delegado para llamar a los controles de forma “thread-safe”. Los métodos asíncronos se lanzan en otro hilo. Si no se tiene en cuenta, al tratar de acceder a los controles del formulario desde un hilo diferente nos dará el siguiente error en tiempo de ejecución:

“Control  nombreControl accessed from a thread other than the thread it was created on.”

Se puede encontrar más información aquí: How to: Make Thread-Safe Calls to Windows Forms Controls

//#1 Delegado para poder manejar los elementos de la interfaz de una forma "Thread-safe"
private delegate void ReturnControlFromSearchDataCallBack(object sender, GetDataCompletedEventArgs e);

GetDBDataModule dataObtainer = new GetDBDataModule();

private void Form1_Load(object sender, EventArgs e)
{
	//Nos suscribimos al evento
	dataObtainer.GetDataCompleted += new GetDataCompletedEventHandler(bl_SearchDataCompleted);
}

...

private void btnSearch_Click(object sender, EventArgs e)
{
	SearchData(txtBox1.Text);
}

private void SearchData(string criteria)
{
	//Llamamos al método asíncrono de la clase.
	dataObtainer.GetDataAsync(criteria);
}

//Método al que se llama cuando se lanza desde el evento.
void bl_SearchDataCompleted(object sender, GetDataCompletedEventArgs e)
{
        //#2 devuelvo el control al formulario, ya que la busqueda se ha ejecutado en otro hilo.
	if (this.InvokeRequired) 	
        {
		ReturnControlFromSearchDataCallBack sdg = 
                    new ReturnControlFromSearchDataCallBack(bl_SearchDataCompleted);
		this.Invoke(sdg, new object[] { sender, e });
	}
	else
	{
		if (e.exception != null)
			System.Windows.Forms.MessageBox.Show(e.exception.Message);
		else
		{
			DataSet ds = e.queryResults;
			FillTheGrid(ds);
		}
	}
}

Y ya está. Formulario principal no se queda “tostado” cuando realizamos llamadas a métodos costosos.

Para terminar:

Como ya he comentado antes, he intentado dejar solo lo referente a la gestión de métodos asíncronos. Se puede hacer de muchas formas y utilizar eventos es tan solo una de ellas, hay más información en el siguiente artículo,donde hablan un poco de todo, delegados, callbacks, etc: .NET Delegates: Making Asynchronous Method Calls in the .NET Environment.

Categorías:.net Etiquetas: , ,

Expresiones Regulares (C#)

Capturando Grupos:

  • Accediendo por índice:
using System.Text.RegularExpressions;

//...

Regex myRegex = new Regex(@".*(reg_exp).*"
   ,RegexOptions.IgnoreCase|RegexOptions.Multiline|...);

MatchCollection myMatchCollection = myRegex.Matches(inputText);

foreach (Match myMatch in myMatchCollection)
{
    string encontrado = myMatch.Groups[0].Value; //accedemos al grupo por indice
}

  • Dando nombre a los grupos:
using System.Text.RegularExpressions;

//...

//dos formas de dar el nombre al grupo.
Regex myRegex =
   new Regex(@".*(?<NombreGrupo>reg_exp)|(?'NombreGrupo2'reg_exp).*"
 ,RegexOptions.IgnoreCase|RegexOptions.Multiline);
MatchCollection myMatchCollection = myRegex.Matches(inputText);

foreach (Match myMatch in myMatchCollection)
{
 string encontrado = myMatch.Groups["NombreGrupo"].Value;
 string encontrado2 = myMatch.Groups["NombreGrupo2"].Value;
}

Recursos:

  • Para probar las espresiones regulares:

http://regexpal.com/

http://erik.eae.net/playground/regexp/regexp.html

Categorías:.net Etiquetas: ,

DataSet to Excel (c#)

21/06/2011 4 comentarios

Buenas,

Para pasar las tablas contenidas en uno o mas DataSet(s).
Es un poco básico ya que no escribo los nombres de las columnas ni nada, pero espero que le sirva de ayuda a alguien. También es probable que haya formas más eficientes de hacer esto, pero puede servir como base.

Acordaros de añadir las refencias en el proyecto.


using Excel = Microsoft.Office.Interop.Excel;
...
public static string DatasetToExcel(List<DataSet> Lds, String name)
        {
            //compruebo a ver si puedo crear el fichero.
            string excelPath = Path.Combine(System.IO.Path.GetTempPath(),name+".xls");
            if (File.Exists(excelPath))
            {
                try
                {
                    File.Delete(excelPath);
                }
                catch (Exception e)
                {
                    throw e;
                }
            }

            //creo el excel
            object misValue = System.Reflection.Missing.Value;
            Excel.Application excelApp;
            Excel.Workbook myworkbook;
            Excel.Worksheet myWorkSheet;

            //nueva aplicación excel.
            excelApp = new Excel.Application();
            
            //añadimos un workbook
            myworkbook = excelApp.Workbooks.Add(Excel.XlWBATemplate.xlWBATWorksheet);

            int worksheetIndex = 1;
            foreach (DataSet ds in Lds)
            {
                foreach (DataTable dt in ds.Tables)
                {
                    //una worksheet para cada tabla nueva.
                    myworkbook.Worksheets.Add(misValue,
excelApp.ActiveWorkbook.Worksheets[excelApp.ActiveWorkbook.Worksheets.Count], worksheetIndex, misValue);
                    
                    //selecciono la última worksheet creada para escribir ahí los datos
                    myWorkSheet = (Excel.Worksheet)myworkbook.Sheets[worksheetIndex];

                    int rowIndex = 1;
                    foreach (DataRow row in dt.Rows)
                    {
                        int columnIndex = 1;
                        
                        foreach (object dc in row.ItemArray)
                        {
                            myWorkSheet.Cells[rowIndex, columnIndex] = dc.ToString();
                            columnIndex++;
                        }
                        rowIndex++;
                    }
                    worksheetIndex++;
                }
            }

            myworkbook.SaveAs(excelPath, Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
            myworkbook.Close(true, misValue, misValue);
            excelApp.Quit();

            return excelPath;
        }

Y recordad…

Categorías:.net Etiquetas: ,

Iteraciones sobre listas con condicionales (Linq)

Supongamos que tenemos una lista de objetos de tipo persona (objeto que puede tener campos como edad, altura, peso, etc.) llamada “personas”
Supongamos que queremos contar cuantas personas que tienen 35 años.

int contador = Personas.Where( p  => p.Edad == 35).Count();

En el método “where” se debe poner una función de tipo booleano que se aplicará a cada elemento de la lista.
El metodo crea automáticamente un objeto del tipo de la lista con el que podremos hacer la comprobación deseada. El nombre del objeto lo ponemos nosotros y puede ser el que nos venga en gana (he puesto ‘p’ como podría haber puesto “persona”).

Si hubiesemos querido devolver una lista con las personas de dicha edad solo tendríamos que haber hecho lo siguiente:

var treintaYtantos = Personas.Where( p => p.Edad == 35).ToList();
Categorías:.net Etiquetas: ,

String to Hex (c#)

Supongamos que tenemos dos campos en una tabla de una BBDD:

-Dato (varchar)= 04000000
-TipoDeDato (varchar): Dword

Lo que nosotros obtenemos son dos string. Si queremos pasar dicho el dato a su verdadero tipo, que es un DWORD, para trabajar con el:

int32 numero = Convert.ToInt32(_value, 16); 
//_value=04000000
//numero= 0x04000000 (67108864 en decimal)

Si ademas el valor en hexadecimal guardado en BBDD estaba en Little Endian, lo podemos pasar a Big endían de la siguiente forma:

int32 numero = IPAddress.HostToNetworkOrder(Convert.ToInt32(HexValueString,16))
//_value=04000000
//numero= 0x00000004 (4 en decimal)

Para pasar el número decimal obtenido a hexadecimal:

string hexString = numero.ToString("X");

Y eso es todo. :-)

Categorías:.net Etiquetas: , ,

Conversiones entre diferentes tipos de listas usando LINQ

String[] to List:

Pongamos que queremos recoger los argumentos de la linea de comandos y guardarlos en una lista.
La función “GetCommandLineArgs()” devuelve un array de strings.
Pongamos también que nos interesa guardarlos todos en minúscula (por si al usuario le da por escribirlos en mayúsculas).

 var parameterList = (from x in Environment.GetCommandLineArgs()
                            select x.ToLower()).ToList();

String to List:

Ahora supongamos que tenemos una clase que lee todo el contenido del fichero y que lo devuelve en un string. Si quisesemos transformar eso en una lista, quitando los saltos de linea y lineas vacías, podríamos probar con lo siguiente.

var urls = (from x in (string[])FileHelper.FileTextRead(inputFile).Split('\n')
               where x != "" && x != "\r"
               select x.TrimEnd('\r')).ToList();

Otro ejemplo, esta vez reemplazando cadenas en el campo devuelto.

var aux = from x in myList
             select Regex.Replace(x,"srchTxt","rplTxt",RegexOptions.IgnoreCase);

DataSet to List:

Si lo que tenemos es un DataSet que contiene una DataTable con una única colunma:

 var myList = (from x in myDataSet.Tables[0].AsEnumerable()
                         select x[0].ToString()).ToList<string>();

DataSet to Dictionary:

Dataset con una DataTable con dos columnas. En cada iteración hay un único elemento, aunque queramos coger dos columnas de la Row. Lo siguiente estaría mal:

//Esto está mal!!
var urls = (from DataRow x in ds.Tables[0].Rows
            select x[0].ToString(),Convert.ToInt32(x[1])).ToDictionary<string,int>();

Lo convertimos en un objeto tipo KeyValuePair, de esa forma seguimos teniendo un único elemento en cada iteración del que podremos obtener los dos objetos deseados.

Dictionary<string, int> urls = (from DataRow x in ds.Tables[0].Rows
                               select new KeyValuePair<string, int>(x[0].ToString(),    
                               (int)x[1])).ToDictionary(kv => kv.Key, kv => kv.Value);

Si hay más de dos elementos que nos interesan en la Row tendríamos que crear una lista o una estructura/clase personalizada. Por el momento no he hecho la prueba.

Categorías:.net Etiquetas: ,

SQL Best Practices (c#)

Forma segura de conectarse a una BBDD. Se controla que no haya una conexión abierta al entrar y se asegura de cerrarla correctamente al salir. Lo ideal al lanzar consultas a las BBDD es hacerlo por medio de procedimientos almacenados, no poniendo la consulta a pelo en el código, ya que es mucho más fácil de mantener/modificar. De hecho, lo ideal es crearse un clase/proyecto aparte con todas las operaciones parametrizadas, de esa forma podremos reutilizar código fácilmente.

Los datos de conexión como la cadena de conexión, timeout, etc. Podrían ir en el fichero de configuración app.config. Esto es algo que se me ocurre a mi, puede que haya una forma mejor o más aconsejable de hacerlo.

SqlConnection connection = new SqlConnection(ConnectionString);

try
 {
    if (connection.State != System.Data.ConnectionState.Open)
        connection.Open();

    //Hacer lo que queramos!
 }
 catch (Exception e)
 {
    throw e; //Manejar la excepción como creamos conveniente
 }
 finally
 {
    if (connection.State != System.Data.ConnectionState.Closed)
    connection.Close();
 }
Categorías:.net Etiquetas: ,

Escribir en un fichero (c#)

Aqui dejo unas lineas de código para escribir en ficheros de forma rápida. Por haber, hay otras formas, éstas son solo un par de ellas.

using System.IO;
...
string path = @"FilePath";
if (!File.Exist(path))
{
    System.IO.StreamWriter file = new System.IO.StreamWriter(path);
    foreach (string s in BufferWithLines)
    {
        file.WriteLine(s);
    }
    file.Close();
}

Aquí va otra. Esta en tan solo dos líneas.

using System.IO;
...
TextWriter tw = new StreamWriter("file");
tw.WriteLine("text");
Categorías:.net Etiquetas: ,
Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.