lunes, 12 de enero de 2009

Obtener instancias de SQL Server y bases de datos disponibles desde .NET (C#)

Por si alguna vez se os ha planteado la necesidad de mostrar un formulario con los orígenes de datos de SQL Server disponibles (algo como el formulario Agregar conexión de Visual Studio) voy a plantear un pequeño ejemplo.

Creamos un nuevo proyecto de Windows Forms al que llamaremos Conexiones, y en él un formulario frmConexiones en el que añadimos 2 combobox: cmbServidores y cmbBasesdeDatos (esta vez me estoy currando los nombres, no os quejaréis :P).

En el código del formulario añadimos lo siguiente:




using System;
using System.Windows.Forms;
using System.Data;
using System.Data.Sql;
using System.Collections.Generic;

namespace Conexiones
{
public partial class frmConexiones : Form
{
SqlDataSourceEnumerator servidores;
System.Data.DataTable tablaServidores;
String servidor;

public frmConexiones()
{
InitializeComponent();
servidores = SqlDataSourceEnumerator.Instance;
tablaServidores = new DataTable();
}
}
}



Ahora hay que añadir el código para el evento click de cada uno de los combobox:
Para obtener la lista de instancias de SQL Server disponibles usaremos el objeto SqlDataSourceEnumerator.Instance, concretamente el método GetDataSources() que nos devuelve un DataTable con las características de las instancias: nombre del servidor, nombre de instancia, si forma parte de un clúster y la versión (8.00.x para SQL Server 2000 y 9.00.x para SQL Server 2005):




private void cmbServidores_Click(object sender, EventArgs e)
{

// Comprobamos que no se haya cargado ya el combobox
if (tablaServidores.Rows.Count == 0)
{
// Obtenemos un dataTable con la información sobre las instancias visibles
// de SQL Server 2000 y 2005
tablaServidores = servidores.GetDataSources();


// Creamos una lista para que sea el origen de datos del combobox
List listaServidores = new List();


// Recorremos el dataTable y añadimos un valor nuevo a la lista con cada fila
foreach (DataRow rowServidor in tablaServidores.Rows)
{

// La instancia de SQL Server puede tener nombre de instancia
//o únicamente el nombre del servidor, comprobamos si hay
//nombre de instancia para mostrarlo
if (String.IsNullOrEmpty(rowServidor["InstanceName"].ToString()))
listaServidores.Add(rowServidor["ServerName"].ToString());
else
listaServidores.Add(rowServidor["ServerName"] + "\\" + rowServidor["InstanceName"]);
}

// Asignamos al origen de datos del combobox la lista con
// las instancias de servidores
this.cmbServidores.DataSource = listaServidores;
}
}



Y para el que muestra las bases de datos tenemos que ejecutar una consulta sobre la vista sys.databases:




private void cmbBasesdeDatos_Click(object sender, EventArgs e)
{
string select;
string cadenaConexion;
List listaBasesdatos = new List();

this.servidor = this.cmbServidores.Text;

// Componemos la cadena de conexión con el servidor seleccionado
// con seguridad integrada
// Si se conecta con usuario de SQL Server hay que cambiar
// la cadena de conexión
cadenaConexion = "Data Source=" + this.servidor + ";Integrated Security=True;Initial Catalog=master";

using (System.Data.SqlClient.SqlConnection con = new System.Data.SqlClient.SqlConnection(cadenaConexion))
{
// Abrimos la conexión
con.Open();

// Obtenemos los nombres de las bases de datos que haya en el servidor
// se pueden filtrar para no mostrar las bases de datos de sistema
select = "select name from sys.databases;";

// Obtenemos un dataReader con el resultado
System.Data.SqlClient.SqlCommand com =
new System.Data.SqlClient.SqlCommand(select, con);
System.Data.SqlClient.SqlDataReader dr = com.ExecuteReader();

// Recorremos el dataReader y añadimos un elemento nuevo
// por cada registro
while (dr.Read())
{
listaBasesdatos.Add(dr[0].ToString());
}

// Asignamos la lista de bases de datos como origen de datos del combobox
this.cmbBasesdeDatos.DataSource = listaBasesdatos;
}
}



Y ya está, con esto tenemos un formulario que permite seleccionar la instancia de SQL Server y la base de datos a la que nos queremos conectar :)

Si en vez de seguridad integrada usamos un usuario y password de SQL Server sólo hay que cambiar la cadena de conexión.

Happy codding ;)

PD: Si no os gusta C# tenéis el mismo código en VB.NET aquí.

11 comentarios:

Oscar Fonseca dijo...

saludos!!

Gracias, veo muy util la informacion y para poder implementarla a un proyecto se me ocurre lo siguiente, agregar un formulario donde el usuario elija el servidor y una base de datos y nos guarde la cadena de coneccion el el archivo de app.config. o bien de alli crear la base de datos cargando un archivo que contenga las santencias sql, Que opinas????

Pablo Bouzada dijo...

Hola Oscar,

tu idea me parece bien, pero creo que tienes que tener en cuenta un par cosas:

- hasta que punto un usuario va a saber qué servidor y base de datos elegir.

- qué modo de acceso vas a usar. Ya que no es lo mismo que se conecten con autenticación Windows, que con un usuario y contraseña de un administrador.

- si guardas la cadena de conexión en el app.config, encríptala porque en el fondo no es más que un xml con texto plano que cualquiera puede leer y escribir.

Espero que te sirva de ayuda ;)

Oscar Fonseca dijo...

Entiendo los puntos que mencionas, Aqui es donde ya me es dificil decidir o la mejor forma de hacerlo, quisas la escasa experiencia que tengo, hasta hoy las aplicaciones que he hecho han sido con base de datos en Sql y donde yo agrego el string de coneccion al archivo xml. por eso es que te lo comente asi.

Q opcion puedes sugerir al respecto? y bien mecionas algo importante hasta donde el usuario es capaz.


gracias por tu articulo nuevamente y contestacion.

Pablo Bouzada dijo...

Oscar, la cadena de conexión la puedes guardar donde quieras, pero es conveniente que esté encriptada.

Otra cosa es a qué tipo de usuario les dejas modificar la información de la cadena de conexión. Por ejemplo, un usuario avanzado (como un administrador) es normal que pueda acceder y modificarla, ya sea desde un formulario o a mano. Pero un usuario "normal", es mejor que elija la base de datos a la que se conecta de una manera más transparente, como un formulario con un combo o grupo de radio button con posibles opciones.

Es cuestión de usar el sentido común :)

Eduardo dijo...

Q tal, comentabas Encriptar la cadena de coneccion, como puedo hacer eso?

Pablo Bouzada dijo...

Eduardo:

http://msdn.microsoft.com/en-us/library/system.configuration.sectioninformation.protectsection.aspx

Anónimo dijo...

Estimado gracias por el ejemplo lo estoy tratando de hacer n C# .net 2010 y tengo el siguiente error

Using the generic type 'System.Collections.Generic.List' requires 1 type arguments

me puedes explicar como resolverlo o enviarme el ejemplo completo

rodcasta@hotmail.com

Saludos
RodCasta

Pablo Bouzada dijo...

Estás usando Generics, cosa que en el ejemplo no se hace. Te recomiendo que profundices un poco sobre los Generics:
http://msdn.microsoft.com/en-us/library/0x6a29h6.aspx

Pero tu error es bastante claro, tienes que pasarle un tipo a la lista que vas a usar.

Anónimo dijo...

Pablo muchas gracias ya lo resolvi

asi esta en tu programa
List listaServidores = new List();
esta es la solucion:
List listaServidores = new List();

gracias por el aporte funciono.

Saludos
RodCasta

carito dijo...

hola, una duda , como seria la cadena de conexion si tengo usuario de SQL Server. Me podrias dar un ejemplo . gracias

carito dijo...

hola, como seria la cadena de conexion si tengo usuario de SQL Server.Me podrias dar un ejemplo. gracias