Conectando con una base de datos SQL SERVER usando ADO.  

 

Por: Demian Panello.

 

 

Conectando con SQL SERVER.

 

En líneas generales, SQL Server es un entorno para la gestión y administración de bases de datos relacionales basado en el lenguaje TRANSACT-SQL, (T SQL).  Este lenguaje es algo asi como una extensión de Microsoft para el lenguaje SQL ANSI.

Junto con ORACLE, SQL SERVER es el entorno de bases de datos relacionales más utilzado en el mundo y detras de ellos vienen MySQL e INFORMIX entre otros.

 

Para poder usar desde una aplicación cliente una base de datos de SQL Server, se necesita un "medio" que interactúe con el servicio de SQL. 

Existen varias tecnologías provistas por Microsoft para el acceso a datos:

ODBC (Open Database Connectivity)

OLE DB (Object Linking and Embedding Databases)

ADO (ActiveX Data Objects)

RDS (Remote Data Service)

Sin entrar en un análisis minucioso de cada una de estas tecnologías, ya que significaría un artículo por si solo, es importante aclarar que ADO está basado en OLE DB. En realidad ADO es OLE DB pero a nivel de aplicación; algo asi como una "capa" superior de acceso a datos, que hace más flexible la interacción entre el cliente y la información.

 

Muy bien, ya se ha visto aquí cómo usar ADO con una base de datos de Access, (Capitulo 14, Aplicaciones usando MFC), y basicamente es lo mismo, sólo cambia el string de conexión.

 

Nos vamos a conectar a la base de datos Northwind que SQL Server trae como ejemplo. En particular tomaremos datos de la tabla Employees cuyos campos son:

 

EmployeeID 
LastName nvarchar 
FirstName nvarchar 
Title nvarchar 
TitleOfCourtesy nvarchar 
BirthDate datetime 
HireDate datetime
Address nvarchar
City nvarchar 
Region nvarchar 
PostalCode nvarchar 
Country nvarchar 
HomePhone nvarchar 
Extension nvarchar
Photo image
Notes ntext 
ReportsTo int 
PhotoPath nvarchar

 

Lo que vamos a hacer es una aplicación que presente un diálogo con un ListBox cargado con todos los apellidos de los empleados de la tabla Employees, luego al seleccionar uno, se mostrará el resto de la información de dicho empleado en diversos EditBox. Algo simple, pero eficiente para mostrar cómo conectar y obtener datos de la base.

 

Comencemos diseñando la aplicación. Cree una aplicación MFC Dialog Based, (yo le puse EjemploSQL1 de nombre) y acepte todos los parámetros por defecto.

Luego diseñe el diálogo más o menos como indica la imágen:

 

 

A continuación el detalle de cada control usado, incluyendo variables miembros asociadas.

 

Control ID Propiedades Variable asociada
CListBox IDC_LISTAPELLIDOS por defecto CListBox m_lstApellidos
CEdit IDC_CORTESIA por defecto CString m_strCortesia
CEdit IDC_FECNA por defecto COleDateTime m_FechaNacimiento
CEdit IDC_TITULO por defecto CString m_strTitulo
CEdit IDC_DIRECCION por defecto CString m_strDireccion
CEdit IDC_CIUDAD por defecto CString m_strCiudad
CEdit IDC_REGION por defecto CString m_strRegion
CEdit IDC_POSTAL por defecto CString m_strPostal
CEdit IDC_PAIS por defecto CString m_strTelefono
CEdit IDC_EXTENSION por defecto CString m_strExtension
CEdit IDC_FECINGRESO por defecto COleDateTime m_strFechaIngreso
CEdit IDC_NOTAS Multiline, AutoVScroll CString m_strNotas

 

Los botones Aceptar y Cancelar quedan como están.

 

Ahora bien, como para usar ADO hay que importar la librería apropiada además de cambiar la propiedad EOF para que no entre en conflicto, yo he armado un archivo .h y un archivo .cpp con funciones para el acceso a ADO. Por lo pronto, sólo la función Conectar y una función para mostrar un error COM.

A continuación el archivo ado_dcp.h ( que deben incluir en el proyecto).

 

#import "c:\Archivos de programa\Archivos comunes\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")

BOOL Conectar(_ConnectionPtr& pCone);

void ver_com_error(_com_error& e);

 

Y su correspondiente ado_dcp.cpp

 

//////////////////////////////////////////////////////////////////////////////////////////////////
// Descr. : Archivo que implementa algunas funciones para usar ADO. 
// Autor : Demian Panello.
// Version : version inicial.
//////////////////////////////////////////////////////////////////////////////////////////////////



#include "StdAfx.h"
#include "ado_dcp.h"

// Constante a modificar de acuerdo a la cadena de conexión a usar.

const LPCSTR STRING_CONNECTION = "Provider=SQLOLEDB;User ID=sa;Initial Catalog=Northwind;Data Source= (local); Integrated Security=SSPI;";


BOOL Conectar(_ConnectionPtr& pCone)
{
   //Cadena para la conexión

  _bstr_t bstrCone(STRING_CONNECTION); 

  try
  {


  //Creo una instancia para el objeto Connection
  pCone.CreateInstance (__uuidof(Connection));

  //Abro la conección.
  pCone->Open (bstrCone ,_bstr_t (""), _bstr_t (""), adModeUnknown );

  return TRUE;
  }
  catch (_com_error& ce) //¿Ocurrió una excepción?
  {
  ver_com_error(ce); //Llamo a la función que procesa los mensajes de error
  return FALSE;
  }
  catch(...)
  {
   AfxMessageBox("Ah ocurrido otro error");
   return FALSE;
  }

}

void ver_com_error(_com_error& e)
{
  CString msgErr; //aquí se almacenará el mensaje de error
  _bstr_t bstrOrigen= e.Source(); 
  _bstr_t bstrDescri=e.Description();  

  msgErr.Format (_T("ADO - COM Error\n\tCódigo = %08lx\n\tOrigen = %s\n\tDescripción = %s\n"),
  e.Error(),(LPCSTR)bstrOrigen, (LPCSTR)bstrDescri);

  msgErr.Format (_T("ADO - COM Error\n\tCódigo = %08lx\n\tOrigen = %s\n\tDescripción = %s\n"),
  e.Error(),(LPCSTR)bstrOrigen, (LPCSTR)bstrDescri);

  AfxMessageBox( msgErr, MB_OK | MB_ICONERROR );

}

 

Note que la constante STRING_CONNECTION almacena el string de conexión apropiado para conectarse a la base de datos Northwind de SQL Server corriendo en la PC local.

 

STRING_CONNECTION = "Provider=SQLOLEDB;User ID=sa;Initial Catalog=Northwind;Data Source= (local); Integrated Security=SSPI;"

 

Puede dirigirse a http://www.connectionstrings.com/ donde encontrará un compendio importante de cadenas de conexión de acuerdo al provedor y/o fuente de datos a usar.

 

Luego agregue la siguiente línea en StdAfx.h:

 

#include "ado_dcp.h"

 

Continuemos con el diálogo.

 

Entonces, como deseamos que el ListBox se cargue con los apellidos de la tabla Employee, tendremos que conectarnos a la base y pedirle los apellidos por medio de una consulta SQL. Esto es correcto, pero si nos adelantamos un poco veremos que nos sería muy útil el campo EmployeeID, ya que nos facilitará muchísmo la tarea de localizar el registro correspondiente al empleado seleccionado cuando justamente se cambie la selección de apellidos en la lista.

Por ésto, al momento de cargar la lista con los apellidos, vamos a asignar en la propiedad ItemData del ListBox el campo EmployeeID, por cada elemento agregado. De esta manera cuando luego se seleccione un apellido, en lugar de tomar el elemento seleccionado de la lista, tomaremos el ItemData del elemento marcado, que es el ID del empleado, y podremos buscar más facíl los restantes datos en la tabla.

 

Cargar la lista lo podemos hacer en la función OnInitDialog(), pero mejor agreguemos una función nueva llamada CargarListaEmpleados() que retorne BOOL, y será está la función que invocaremos desde OnInitDialog().

 

BOOL CEjemploSQL1Dlg::CargarListaEmpleados()
{
  BOOL bRespuesta = TRUE;
  CString Apellido;
  long idEmpleado;
  int pos;
  long FirstId;

  _ConnectionPtr pAdoCone; // Objeto para la conexión

  _RecordsetPtr Rs(__uuidof(Recordset)); // Un objeto recordset

  if(Conectar(pAdoCone)) 
  {
    // Se pudo establecer la conexión.

   Rs->PutRefActiveConnection(pAdoCone);


   _bstr_t bstrSql(_T("Select EmployeeID, LastName from Employees order by LastName"));

   try
    {
    Rs->Open(bstrSql, vtMissing, adOpenStatic, adLockReadOnly, adCmdUnknown);

    m_lstApellidos.ResetContent();

   SetCursor(LoadCursor(NULL,IDC_WAIT));

   while(!Rs->adoEOF)
   {
      idEmpleado = Rs->GetFields()->GetItem("EmployeeID")->Value.iVal;

     Apellido = Rs->GetFields()->GetItem("LastName")->Value.bstrVal;

     pos = m_lstApellidos.AddString (Apellido);

     m_lstApellidos.SetItemData(pos, idEmpleado); 

    Rs->MoveNext(); 
   }

  if (m_lstApellidos.GetCount() > 0)
  { 
   FirstId = m_lstApellidos.GetItemData(0); 
   m_lstApellidos.SetCurSel(0); 

   ObtenerDatosEmpleado(FirstId);
  }

  Rs->Close();

  // Se cierra la conexión.
  pAdoCone->Close();
  }
  catch (_com_error& ce) //¿Ocurrió una excepción?
 {
  SetCursor(LoadCursor(NULL,IDC_ARROW));

  ver_com_error(ce); //Llamo a la función que procesa los mensajes de error
  bRespuesta = FALSE;
  }
  catch(...)
  {
   AfxMessageBox("Otro error");
   }
 }
 else
 {  
  AfxMessageBox("Error al intentar conectar con SQL Server");
  bRespuesta = FALSE;
  }

return bRespuesta;
}

 

Y en OnInitDialog() llamamos a la función:

 

// Ahora se intentará conectar con la base Northwind en SQL Server
// y obtener todos los registros de la tabla Employee.


if( !CargarListaEmpleados())
AfxMessageBox("Error al cargar la lista de empleados");


UpdateData(FALSE);

 

Agregamos el mensaje LBN_SELCHANGE para el ListBox y en OnSelChangeListaapellidos() escribimos

 

void CEjemploSQL1Dlg::OnSelchangeListapellidos() 
{
long id;
id = m_lstApellidos.GetItemData(m_lstApellidos.GetCurSel());  // se obtiene el ItemData del elemento seleccionado
// Se llama a una función que obtiene los datos del empleado del id pasado como parámetro.
ObtenerDatosEmpleado(id);
}

 

Al marcar un apellido, se obtiene el EmployeeID que está en el ItemData y se lo pasa a una función que buscará ese registro en la tabla de Employees.

Entonces agregue la función ObtenerDatosEmpleado(long idEmpleado) al diálogo.

 

void CEjemploSQL1Dlg::ObtenerDatosEmpleado(long idEmpleado)
{
   CString dato;

  _ConnectionPtr pAdoCone; // Objeto para la conexión

  _RecordsetPtr Rs(__uuidof(Recordset)); // Un objeto recordset

  if(Conectar(pAdoCone)) 
  {
    // Se pudo establecer la conexión.

   // Se le indica al recordset cuál es el objeto connection actual
   Rs->PutRefActiveConnection(pAdoCone);

   _bstr_t bstrSql;

   dato.Format("%ld", idEmpleado); 

   bstrSql = "Select * from Employees where EmployeeID = " + dato;

   try
  {
   Rs->Open(bstrSql, vtMissing, adOpenStatic, adLockReadOnly, adCmdUnknown);

   m_strNombre = Rs->GetFields()->GetItem("FirstName")->Value.bstrVal; 
   m_strCiudad = Rs->GetFields()->GetItem("City")->Value.bstrVal;
   m_strDireccion = Rs->GetFields()->GetItem("Address")->Value.bstrVal; 
   m_strCortesia = Rs->GetFields()->GetItem("TitleOfCourtesy")->Value.bstrVal; 
   m_strExtension = Rs->GetFields()->GetItem("Extension")->Value.bstrVal; 
   m_strNotas = Rs->GetFields()->GetItem("Notes")->Value.bstrVal; 
   m_strPais = Rs->GetFields()->GetItem("Country")->Value.bstrVal; 
   m_strRegion = Rs->GetFields()->GetItem("Region")->Value.bstrVal; 
   m_strTelefono = Rs->GetFields()->GetItem("HomePhone")->Value.bstrVal; 
   m_strTitulo = Rs->GetFields()->GetItem("Title")->Value.bstrVal; 
   m_strPostal = Rs->GetFields()->GetItem("PostalCode")->Value.bstrVal; 

   m_FechaNacimiento = Rs->GetFields()->GetItem("BirthDate")->Value; 
   m_FechaIngreso = Rs->GetFields()->GetItem("HireDate")->Value; 

   Rs->Close();
  }
  catch (_com_error& ce) //¿Ocurrió una excepción?
  {
    ver_com_error(ce); //Llamo a la función que procesa los mensajes de error
  }

  pAdoCone->Close();

  UpdateData(FALSE);
  }
  else
      AfxMessageBox("Error al conctar con SQL Server");
}

 

Para terminar, como todos los objetos de ADO son objetos COM, hay que inicializar COM en el constructor y desinicializar al momento de destruir el diálogo.

Escriba en el constructor del diálogo:

CoInitialize(NULL);

 

Y agregue el mensaje WM_DESTROY al diálogo y escriba:

 

CoUninitialize();

 

Si el servicio de SQL está corriendo, puede ejecutar la aplicación, obtendrá una salida como la siguiente:

 

 

Descargar fuente de los ejemplos: EjemploSQL1.rar (72 Kb).

 

Volver a la página principal