25 junio 2010

SQLite con C# desde Windows Forms



Después de ver cómo crear una aplicación de consola en C# que cree una base de datos con SQLite, guarde datos y los lea, vamos a ver cómo hacer algo similar en una aplicación "de ventana", usando Windows Forms.

Lo haremos usando componentes "normales", en vez de los predefinidos que permiten acceder a bases de datos. Tendremos una ventana principal, que muestre los datos actuales en un ListView, y que incluya un botón para añadir datos nuevos:


El botón de "Añadir" mostrará un segundo formulario, que será el que pida los datos:


Lo podemos hacer dando los siguientes pasos (en Visual C# 2008):
  • Crear un nuevo proyecto, que sea una "Aplicación de Windows Forms"
  • En la ventana principal, añadir un ListView y un botón.
  • Personalizar el ListView usando la pestaña de propiedades, de modo que tenga dos columnas (una para el código y otra para el nombre), y que como modo de visualización ("View") tenga el de "detalles" ("Details").
  • Crear una segunda ventana, desde el menú "Proyecto", en la opción "Agregar Windows Forms".
  • Añadir a esa ventana dos TextBox para los textos que introducirá el usuario, junto con sus Label para los textos aclaratorios y un botón para confirmar.
  • Añadir el fichero DLL a las referencias del proyecto (en la ventana del "Explorador de soluciones", pulsando el botón derecho sobre "References", y escogiendo el fichero System.Data.SQLite.DLL desde la pestaña "Examinar").
  • Completar el código fuente del proyecto.
En cuanto al código, las dos partes claves son la parte que lee los datos para rellenar el ListView y la parte que lanza la ventana auxiliar, toma de ella lo que teclee el usuario y después introduce esos datos.

La de rellenar el ListView (que se llamaría al final del contructor, y después de cada introducción de un nuevo dato) podría ser así:






 1:         private void ActualizarListaCiudades()
2: {

3: conexion =
4: new SQLiteConnection
5: ("Data Source=personal.sqlite;Version=3;New=False;Compress=True;");

6: conexion.Open();
7:
8: lstCiudades.Items.Clear();

9:
10: // Lanzamos la consulta y preparamos la estructura para leer datos
11:
string consulta = "select * from ciudad";
12: SQLiteCommand cmd = new SQLiteCommand(consulta, conexion);

13: SQLiteDataReader datos = cmd.ExecuteReader();
14: // Leemos los datos de forma repetitiva
15:
while (datos.Read())

16: {
17: string codigo = Convert.ToString(datos[0]);

18: string nombre = Convert.ToString(datos[1]);
19: // Y los mostramos

20:
ListViewItem item = new ListViewItem(codigo);
21: item.SubItems.Add(nombre);

22: lstCiudades.Items.Add(item);
23: }
24: conexion.Close();

25: }


Y la de añadir otro dato podría sería así:


 1:         private void InsertarDatos(string codigo, string nombre)
2: {

3:
4: string insercion; // Orden de insercion, en SQL
5:
SQLiteCommand cmd; // Comando de SQLite

6:

7: conexion =
8: new SQLiteConnection
9: ("Data Source=personal.sqlite;Version=3;New=False;Compress=True;");

10: conexion.Open();
11:
12: try
13: {

14: insercion = "INSERT INTO ciudad " +
15: "VALUES ('"+codigo+"','"+nombre+"');";

16: cmd = new SQLiteCommand(insercion, conexion);
17: cmd.ExecuteNonQuery();

18: }
19: catch (Exception e)
20: {
21: MessageBox.Show(

22: "No se ha podido insertar. Posiblemente un codigo esta repetido",
23: "Aviso");
24: }
25: conexion.Close();

26: }


De modo que la acción completa sobre el botón de "Añadir" sería algo como

1:         private void btAnadir_Click(object sender, EventArgs e)
2: {

3: ventanaAnadir.Limpiar();
4: ventanaAnadir.ShowDialog();

5: InsertarDatos(ventanaAnadir.GetCodigo(), ventanaAnadir.GetNombre());

6: ActualizarListaCiudades();
7: }


Y aquí puedes descargar el proyecto, con los dos fuentes, los ficheros DLL y el ejecutable: csharp_sqlite2.zip



13 junio 2010

SQLite desde C#

SQlite es un gestor de bases de datos, que permite hacer consultas usando el lenguaje SQL, y que se puede incluir como parte de un programa en C o C++, apenas incluyendo un fichero DLL adicional... o incluso ni eso, ya que realmente es una biblioteca que se puede "incrustar" como parte del programa.

Desde C# no puede usar la DLL de SQLite directamente, pero sí a través de otras como System.Data.Sqlite.Dll, que se puede encontrar en

http://sqlite.phxsoftware.com/

Con esa DLL, junto con la original de SQLite, podremos hacer programas en C# que nos permitan acceder a bases de datos y que sean fáciles de distribuir. SQLite se puede encontrar en

http://www.sqlite.org/

Vamos a ver un pequeño ejemplo de cómo crear una base de datos, cómo introducir datos y cómo mostrarlos. Primero veremos los fragmentos de código, luego un fuente completo, después cómo compilarlo y probarlo, y finalmente incluiré la descarga completa, con fuentes, ejecutable y DLLs.

Para conectar con la base de datos, crearíamos un objeto de tipo "SQLiteConnection":


conexion =
new SQLiteConnection
("Data Source=personal.sqlite;Version=3;New=True;Compress=True;");
conexion.Open();


Para consultas de creación y modificación, crearemos un objeto de tipo SQLiteCommand y lo ejecutaremos con "ExecuteNonQuery()":


string creacion = "CREATE TABLE ciudad "
+"(codigo VARCHAR(2) PRIMARY KEY, nombre VARCHAR(30));";
SQLiteCommand cmd = new SQLiteCommand(creacion, conexion);
cmd.ExecuteNonQuery();


Si se trata de una consulta que sí devuelve datos (un "select"), usaremos "ExecuteReader()" y leeremos datos de forma repetitiva con "while (datos.Read())"


string consulta = "select * from ciudad";
SQLiteCommand cmd = new SQLiteCommand(consulta, conexion);
SQLiteDataReader datos = cmd.ExecuteReader();
// Leemos los datos de forma repetitiva
while (datos.Read())
{
string codigo = Convert.ToString(datos[0]);
string nombre = Convert.ToString(datos[1]);
// Y los mostramos
Console.WriteLine("Codigo: {0}, Nombre: {1}",
codigo, nombre);
}


En los "ExecuteNonQuery", podemos comprobar la cantidad de datos afectados, lo que nos puede ayudar a descubrir errores:


cantidad = cmd.ExecuteNonQuery();
if (cantidad < 1)
Console.WriteLine("No se ha podido insertar");



Aun así, muchos tipos de errores, como una clave duplicada, pueden provocar que salte una excepción y se interrumpa el programa, por lo que sería recomendable incluir las órdenes de inserción dentro de un bloque "try...catch"


En cualquier caso, al terminar de acceder deberemos cerrar la conexión a la base de datos:


conexion.Close();





El fuente completo podría ser así:

// Ejemplo de acceso a SQLite desde C#
// Nacho Cabanes, junio 2010

using System;
using System.IO;
using System.Data.SQLite; //Utilizamos la DLL


public class pruebaSQLite01
{
static SQLiteConnection conexion;

private static void CrearBBDDSiNoExiste()

{
if (!File.Exists("personal.sqlite") )

{
// Creamos la conexion a la BD.
// El Data Source contiene la ruta del archivo de la BD
conexion =
new SQLiteConnection
("Data Source=personal.sqlite;Version=3;New=True;Compress=True;");

conexion.Open();

// Creamos la primera tabla
string creacion = "CREATE TABLE ciudad "
+"(codigo VARCHAR(2) PRIMARY KEY, nombre VARCHAR(30));";

SQLiteCommand cmd = new SQLiteCommand(creacion, conexion);
cmd.ExecuteNonQuery();

// Creamos la segunda tabla

creacion = "CREATE TABLE persona "
+"(codigo VARCHAR(2) PRIMARY KEY, nombre VARCHAR(30),"
+" codigoCiudad VARCHAR(2) );";
cmd = new SQLiteCommand(creacion, conexion);

cmd.ExecuteNonQuery();
}
else
{
// Creamos la conexion a la BD.

// El Data Source contiene la ruta del archivo de la BD
conexion =
new SQLiteConnection
("Data Source=personal.sqlite;Version=3;New=False;Compress=True;");
conexion.Open();

}
}


private static void InsertarDatos()

{

string insercion; // Orden de insercion, en SQL
SQLiteCommand cmd; // Comando de SQLite
int cantidad; // Resultado: cantidad de datos


try
{
insercion = "INSERT INTO ciudad "+
"VALUES ('t','Toledo');";
cmd = new SQLiteCommand(insercion, conexion);

cantidad = cmd.ExecuteNonQuery();
if (cantidad < 1)

Console.WriteLine("No se ha podido insertar");

insercion = "INSERT INTO ciudad "+
"VALUES ('a','Alicante');";

cmd = new SQLiteCommand(insercion, conexion);
cantidad = cmd.ExecuteNonQuery();

if (cantidad < 1)
Console.WriteLine("No se ha podido insertar");


insercion = "INSERT INTO persona "+
"VALUES ('j','Juan','t');";
cmd = new SQLiteCommand(insercion, conexion);

cantidad = cmd.ExecuteNonQuery();
if (cantidad < 1)

Console.WriteLine("No se ha podido insertar");

insercion = "INSERT INTO persona "+
"VALUES ('p','Pepe','t');";

cmd = new SQLiteCommand(insercion, conexion);
cantidad = cmd.ExecuteNonQuery();

if (cantidad < 1)
Console.WriteLine("No se ha podido insertar");

}
catch (Exception e)
{
Console.WriteLine("No se ha podido insertar");

Console.WriteLine("Posiblemente un código está repetido");
Console.WriteLine("Error encontrado: {0} ", e.Message);

}
}

private static void MostrarCiudades()

{
// Lanzamos la consulta y preparamos la estructura para leer datos
string consulta = "select * from ciudad";
SQLiteCommand cmd = new SQLiteCommand(consulta, conexion);

SQLiteDataReader datos = cmd.ExecuteReader();
// Leemos los datos de forma repetitiva
while (datos.Read())

{
string codigo = Convert.ToString(datos[0]);

string nombre = Convert.ToString(datos[1]);
// Y los mostramos

Console.WriteLine("Codigo: {0}, Nombre: {1}",
codigo, nombre);
}

}


private static void MostrarPersonas()
{

// Lanzamos la consulta y preparamos la estructura para leer datos
string consulta = "select * from persona";
SQLiteCommand cmd = new SQLiteCommand(consulta, conexion);

SQLiteDataReader datos = cmd.ExecuteReader();
// Leemos los datos de forma repetitiva
while (datos.Read())

{
string codigo = Convert.ToString(datos[0]);

string nombre = Convert.ToString(datos[1]);
// Y los mostramos

Console.WriteLine("Codigo: {0}, Nombre: {1}",
codigo, nombre);
}

}

private static void MostrarCiudadesYPersonas()
{

// Lanzamos la consulta y preparamos la estructura para leer datos
string consulta =
"select persona.nombre, ciudad.nombre "+
"from persona, ciudad "+
"where persona.codigoCiudad = ciudad.Codigo";

SQLiteCommand cmd = new SQLiteCommand(consulta, conexion);
SQLiteDataReader datos = cmd.ExecuteReader();

// Leemos los datos de forma repetitiva
while (datos.Read())
{
string nombrePersona = Convert.ToString(datos[0]);

string nombreCiudad = Convert.ToString(datos[1]);
// Y los mostramos

Console.WriteLine("{0} vive en {1}",
nombrePersona, nombreCiudad);
}

}

private static void CerrarBBDD()
{

conexion.Close();
}


public static void Main()

{
Console.WriteLine("Accediendo...");
CrearBBDDSiNoExiste();

Console.WriteLine("Añadiendo datos...");
InsertarDatos();
Console.WriteLine("Datos de ciudades:");

MostrarCiudades();
Console.WriteLine("Datos de personas:");
MostrarPersonas();

Console.WriteLine("Datos de personas y ciudades:");
MostrarCiudadesYPersonas();
CerrarBBDD();

}

}






Para compilar este fuente, deberemos incluir "System.Data.SqLite.dll" dentro de las referencias del proyecto. Esto se haría desde el propio entorno visual si usamos herramientas como Visual Studio o SharpDevelop, o bien añadiendo la opción /r si compilamos desde línea de comandos, por ejemplo usando Mono:

gmcs pruebaSQLite.cs /r:System.Data.SqLite.dll







Y aquí puedes descargar el fuente, los ficheros DLL y el ejecutable: csharp_sqlite.zip



07 junio 2010

Jugando con C#

Si quieres aprender C# desde cero (o casi), puedes consultar mi curso introductorio, en:

http://www.nachocabanes.com/csharp/curso/


También hay un foro donde consultar dudas sobre C#, en AprendeAProgramar.com, en el que es fácil encontrarme:

http://www.aprendeaprogramar.com/mod/forum/view.php?id=372

Pero a veces experimento con otras temas, no tan "para principiantes", como puede ser el acceso a bases de datos desde C# usando SQLite o la creación de juegos con Tao.Sdl. Muchos de esos temas quedarán publicados aquí, y sólo los más básicos se abrirán camino hasta aparecer como parte del curso.

Si te interesa... asómate durante los próximos días... ;-)