Este es un tema polémico del que se habla mucho y nada, digo que se habla mucho porque al buscar algo

de información en Internet, uno se da cuenta, que esta plagado de sitios donde preguntan como aplicar programación en 3 capas, o N-Capas, pero en muy pocos lugares se responde con algo cierto y concreto, la mayoría hacen referencia a libros gordos que tardarías en leer semanas (no estoy en contra de la lectura, es un proceso largo nada más y casi todos buscamos aprenderlo un poco más rápido). Este artículo también será bastante largo y me aventuro a decir que me tomará varias noches escribirlo completamente, pero no será nada comparado con un libro con un lomo de 15 centímetros La primer gran confusión que noto, es que la mayoría no sabe diferenciar entre los conceptos 1. Arquitectura de 3 capas: se basa más bien en como será construido el entorno, una manera de decirlo en romper el clásico concepto Cliente-Servidor para introducir conceptos como Back End (Base de Datos), Middleware (Servidor de Aplicaciones), Front End (Interfaz de Usuario). Este es un concepto grande que no veremos ahora, pero lo remarco para hacer entender que no tiene nada que ver con la programación en capas. Se acerca más a un concepto físico. 2. Programación en 3 (n) capas: este es el tema en cuestión y estira más hacia un concepto lógico. En cómo partimos, agrupamos, clasificamos, optimizamos nuestro código. El mismo introduce conceptos como Capa de Acceso a Datos (Esta nos permite conectarnos a la fuente de datos y operar contra ella), Capa de Negocios (es la que se encarga de procesar todo, validaciones, etc. la misma suele distribuirse en la aplicación en sí y en la BBDD), y Capa de Presentación (es más bien lo que el usuario percibe, su interfaz gráfica por lo gral). Creo que con esos conceptos introductorios ya estamos preparados para comprender mejor ciertos aspectos de este paradigma. Para resaltar por último, gracias a la separación en capas quiere decir que podemos cambiar de proveedor de base de datos, y no necesitaremos reescribir toda la aplicación de vuelta, sino solamente esa pequeña capa y reutilizaríamos la interfaz y las reglas de negocios, o también podemos mantener las reglas de negocios y el motor de base de datos, y fácilmente cambiarnos de una interfaz WinForm a WebForm, siempre la más dura de cambiar en la de negocios ya que afecta en un nivel mínimo a las otras 2 capas. Creo que ya es suficiente teoría de momento y podemos comenzar con la acción, el código que voy a ir escribiendo lo haré en Visual Studio 2010 por que es la que tengo instalada ahora mismo en la maquina, pero funciona desde la versión 2005 con el framework 2.0 en adelante, ya que ADO.Net no ha sufrido grandes cambios desde esa versión, así que ustedes lo pueden ir creando en la versión del IDE o framework que más gusten. Primeramente vamos a crear una solución con un proyecto de Biblioteca de Clases, el mismo tendrá 3 clases principales para representar la capa de Acceso a Datos, la primera llamaremos GDatos.cs, la misma le asignaremos el namespace AccesoDatos, y una clase abstracta con el mismo nombre. Haremos uso de estos 2 namespace:
1 using System;

2 using System.Data;

Lo siguiente que haremos será estructurar en código con regiones para una mayor comodidad en la lectura del mismo, la primer región es la de Declaración de Variables en la misma creamos las variables o atributos para la conexion a la BBDD, más un objeto de interfaz de conexión para que sea implementada de manera específica por la clase hija, si se han dado cuenta estamos usando ya conceptos de OOP avanzados, y lo seguiremos usando fuertemente en el transcurso del artículo. Esta es una clase que obligatoriamente debe ser hereda por otra. El nivel de acceso por eso están definidas como protected para que sean modificadas por si misma o por sus clases derivadas.
1 2 3 4 5 6 7 8 9 10 #region "Declaración de Variables" protected protected protected protected protected protected #endregion string MServidor = ""; string MBase = ""; string MUsuario = ""; string MPassword = ""; string MCadenaConexion = ""; IDbConnection MConexion;

Lo siguiente por hacer es muy sencillo, crear los setters y getters de nuestros atributos anteriormente definidos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #region "Setters y Getters" // Nombre del equipo servidor de datos. public string Servidor { get { return MServidor; } set { MServidor = value; } } // end Servidor // Nombre de la base de datos a utilizar. public string Base { get { return MBase; } set { MBase = value; } } // end Base // Nombre del Usuario de la BD. public string Usuario { get { return MUsuario; } set { MUsuario = value; } } // end Usuario // Password del Usuario de la BD. public string Password { get { return MPassword; } set { MPassword = value; } } // end Password // Cadena de conexión completa a la base. public abstract string CadenaConexion { get; set; }

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

#endregion #region "Privadas" // Crea u obtiene un objeto para conectarse a la base de datos. protected IDbConnection Conexion { get { // si aun no tiene asignada la cadena de conexion lo hace if (MConexion == null) MConexion = CrearConexion(CadenaConexion); // si no esta abierta aun la conexion, lo abre if (MConexion.State != ConnectionState.Open) MConexion.Open(); // retorna la conexion en modo interfaz, para que se adapte a cualquier implementacion de los distintos fabricantes de motores de bases de datos return MConexion; } // end get } // end Conexion #endregion

Creamos ahora los métodos para hacer lecturas a la fuente de datos, lo hacemos ya en esta clase porque son metodos generales que pueden implementar tal cual las clases hijas. En el caso de los DataReader que son muy especificos del driver utilizados, vamos a utilizar el objeto IDataReader que es una interfaz de implementación general.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #region "Lecturas" // Obtiene un DataSet a partir de un Procedimiento Almacenado. public DataSet TraerDataSet(string procedimientoAlmacenado) { var mDataSet = new DataSet(); CrearDataAdapter(procedimientoAlmacenado).Fill(mDataSet); return mDataSet; } // end TraerDataset //Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parámetros. public DataSet TraerDataSet(string procedimientoAlmacenado, params Object[] args) { var mDataSet = new DataSet(); CrearDataAdapter(procedimientoAlmacenado, args).Fill(mDataSet); return mDataSet; } // end TraerDataset // Obtiene un DataSet a partir de un Query Sql. public DataSet TraerDataSetSql(string comandoSql) { var mDataSet = new DataSet(); CrearDataAdapterSql(comandoSql).Fill(mDataSet); return mDataSet;

params object[] args) { var com = Comando(procedimientoAlmacenado). // recorrer los parametros del SP foreach (IDbDataParameter par in com.ExecuteReader(). } // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado.ExecuteNonQuery(). return com.Copy(). } // end TraerDataTable //Obtiene un DataTable a partir de un Query SQL public DataTable TraerDataTableSql(string comandoSql) { return TraerDataSetSql(comandoSql). params Object[] args) { return TraerDataSet(procedimientoAlmacenado.ExecuteReader(). Solo funciona con SP's que tengan // definida variables de tipo output.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Tables[0]. } // end TraerDataTable //Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parámetros. args). } // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado y sus parámetros. return com.InputOutput . public IDataReader TraerDataReaderSql(string comandoSql) { var com = ComandoSql(comandoSql). } // end TraerDataTableSql // Obtiene un DataReader a partir de un Procedimiento Almacenado. public IDataReader TraerDataReader(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado). para funciones escalares mas abajo se declara un metodo public object TraerValorOutput(string procedimientoAlmacenado) { // asignar el string sql al command var com = Comando(procedimientoAlmacenado).Copy().28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 } // end TraerDataSetSql // Obtiene un DataTable a partir de un Procedimiento Almacenado. // ejecutar el command com. public DataTable TraerDataTable(string procedimientoAlmacenado) { return TraerDataSet(procedimientoAlmacenado). } // end TraerDataReaderSql // Obtiene un Valor Escalar a partir de un Procedimiento Almacenado.ExecuteReader().Direction == ParameterDirection.Copy(). args). // declarar variable de retorno Object resp = null. CargarParametros(com. public DataTable TraerDataTable(string procedimientoAlmacenado. return com.Tables[0]. public IDataReader TraerDataReader(string procedimientoAlmacenado.Tables[0].

// ejecutar el command com.Value.ExecuteScalar(). } // end TraerValor // Obtiene un Valor Escalar a partir de un Procedimiento Almacenado.89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 || par.ExecuteNonQuery(). public object TraerValorEscalar(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado). return resp. con Params de Entrada . } // end TraerValorEscalar /// Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado. } // end TraerValor // Obtiene un Valor a partir de un Procedimiento Almacenado. // cargar los parametros del SP CargarParametros(com.Direction == ParameterDirection. // recorrer los parametros del SP foreach (IDbDataParameter par in com. } // end TraerValor // Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado.Direction == ParameterDirection.Output) resp = par.Output) resp = par. return com. y sus parámetros.Output) resp = par.ExecuteNonQuery(). public object TraerValorOutputSql(string comadoSql) { // asignar el string sql al command var com = ComandoSql(comadoSql).InputOutput || par. // recorrer los parametros del Query (uso tipico envio de varias sentencias sql en el mismo command) foreach (IDbDataParameter par in com. return resp. public object TraerValorOutput(string procedimientoAlmacenado. return resp. params Object[] args) { // asignar el string sql al command var com = Comando(procedimientoAlmacenado).InputOutput || par.Direction == ParameterDirection.Direction == ParameterDirection.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection. // ejecutar el command com. // declarar variable de retorno Object resp = null. // declarar variable de retorno Object resp = null.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par. args).Value.Value.

// metodo sobrecargado para autenticarse contra el motor de BBDD public bool Autenticar() { if (Conexion. return com. args).Open(). command. }// end Autenticar // cerrar conexion public void CerrarConexion() { if (Conexion.Open().State != ConnectionState.State != ConnectionState. return true.Close(). params object[] args) { var com = Comando(procedimientoAlmacenado). MConexion = CrearConexion(CadenaConexion). string vPassword) { MUsuario = vUsuario. protected abstract IDataAdapter CrearDataAdapterSql(string comandoSql). para que las clases derivadas estén obligadas a implementarlas a su manera.ExecuteScalar().public Object TraerValorEscalar(string procedimientoAlmacenado.Closed) MConexion. son muy específicos y deben ser implementados por cada una. return true. ya que los objetos connection. MPassword = vPassword. Object[] args). params Object[] args). } // end TraerValorEscalar // Obtiene un Valor de una funcion Escalar a partir de un Query SQL public object TraerValorEscalarSql(string comandoSql) { var com = ComandoSql(comandoSql). . en un modo especifico. dataadapter. al inicio tendremos varios métodos abstractos.Open) Conexion. return com. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #region "Acciones" protected abstract IDbConnection CrearConexion(string cadena). MConexion. CargarParametros(com. protected abstract void CargarParametros(IDbCommand comando. protected abstract IDbCommand Comando(string procedimientoAlmacenado).ExecuteScalar(). protected abstract IDataAdapter CrearDataAdapter(string procedimientoAlmacenado. protected abstract IDbCommand ComandoSql(string comandoSql). }// end Autenticar // metodo sobrecargado para autenticarse contra el motor de BBDD public bool Autenticar(string vUsuario. } // end TraerValorEscalarSql #endregion El siguiente bloque es para ejecutar procesos que no devuelven valores.

Direction == ParameterDirection. }// end try finally { EnTransaccion = false. no se utiliza normalmente en todos lados desde la aplicación. //Comienza una Transacción en la base en uso. }// end for return resp. i < com.1).ExecuteNonQuery(). CargarParametros(com. podemos crearlo de este modo: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #region "Transacciones" protected IDbTransaction MTransaccion.Parameters[i]. params Object[] args) { var com = Comando(procedimientoAlmacenado). public int Ejecutar(string procedimientoAlmacenado.Count. public int Ejecutar(string procedimientoAlmacenado) { return Comando(procedimientoAlmacenado). for (var i = 0.ExecuteNonQuery(). if (par.Direction == ParameterDirection. args). protected bool EnTransaccion.} // end CerrarConexion 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 // Ejecuta un Procedimiento Almacenado en la base. var resp = com. EnTransaccion = true. no podemos olvidarnos de la sección transaccional. i++) { var par = (IDbDataParameter)com.Output) args. pero en procesos dependientes es necesario. public void IniciarTransaccion() { try { MTransaccion = Conexion. así que si necesitamos usarlo. } // end Ejecutar #endregion Ahora bien.BeginTransaction().InputOutput || par.Value. public void TerminarTransaccion() { try .Parameters. } }// end IniciarTransaccion //Confirma la transacción activa.SetValue(par. } // end Ejecutar // Ejecuta un query sql public int EjecutarSql(string comandoSql) { return ComandoSql(comandoSql). utilizando los parámetros. i . } // end Ejecutar //Ejecuta un Procedimiento Almacenado en la base.ExecuteNonQuery().

string MCadenaConexion = "". EnTransaccion = false.23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 { MTransaccion. } } // end Base // Nombre del Usuario de la BD. . } finally { MTransaccion = null. } set { MBase = value. using System. namespace AccesoDatos { public abstract class GDatos { #region "Declaración de Variables" protected protected protected protected protected protected #endregion #region "Setters y Getters" // Nombre del equipo servidor de datos. string MPassword = "". } set { MServidor = value. public string Base { get { return MBase. }// end finally }// end TerminarTransaccion //Cancela la transacción activa. string MServidor = "". IDbConnection MConexion. string MUsuario = "". }// end finally }// end AbortarTransaccion #endregion El código completo lo pueden ver aqui: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using System. public void AbortarTransaccion() { try { MTransaccion. string MBase = "". EnTransaccion = false. } finally { MTransaccion = null.Rollback().Data.Commit(). } } // end Servidor // Nombre de la base de datos a utilizar. public string Servidor { get { return MServidor.

Open(). lo hace . } } // end Password // Cadena de conexión completa a la base. // retorna la conexion en modo interfaz. CrearDataAdapter(procedimientoAlmacenado). para que se adapte a cualquier implementacion de los distintos fabricantes de motores de bases de datos return MConexion.State != ConnectionState.Open) MConexion. } } // end Usuario // Password del Usuario de la BD. } // end get } // end Conexion #endregion #region "Lecturas" // Obtiene un DataSet a partir de un Procedimiento Almacenado.36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 public string Usuario { get { return MUsuario. } // end TraerDataset //Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parámetros. protected IDbConnection Conexion { get { // si aun no tiene asignada la cadena de conexion if (MConexion == null) MConexion = CrearConexion(CadenaConexion). lo abre if (MConexion. // si no esta abierta aun la conexion. } #endregion #region "Privadas" // Crea u obtiene un objeto para conectarse a la base de datos.Fill(mDataSet ). } set { MPassword = value. public abstract string CadenaConexion { get. public string Password { get { return MPassword. set. return mDataSet. public DataSet TraerDataSet(string procedimientoAlmacenado) { var mDataSet = new DataSet(). public DataSet TraerDataSet(string procedimientoAlmacenado. } set { MUsuario = value.

Tables[0].ExecuteReader().97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 params Object[] args) { var mDataSet = new DataSet(). CrearDataAdapter(procedimientoAlmacenado. params Object[] args) { return TraerDataSet(procedimientoAlmacenado. } // end TraerDataTable //Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parámetros. } // end TraerDataset // Obtiene un DataSet a partir de un Query Sql. public IDataReader TraerDataReader(string procedimientoAlmacenado.Tables[0]. } // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado. } // end TraerDataTableSql // Obtiene un DataReader a partir de un Procedimiento Almacenado.Fill(mDataSet). args). return mDataSet. } // end TraerDataTable //Obtiene un DataTable a partir de un Query SQL public DataTable TraerDataTableSql(string comandoSql) { return TraerDataSetSql(comandoSql). args). return com. } // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado y sus parámetros.Copy(). } // end TraerDataSetSql // Obtiene un DataTable a partir de un Procedimiento Almacenado. CargarParametros(com. public DataTable TraerDataTable(string procedimientoAlmacenado. public IDataReader TraerDataReaderSql(string comandoSql) { . params object[] args) { var com = Comando(procedimientoAlmacenado). return mDataSet. return com. public DataSet TraerDataSetSql(string comandoSql) { var mDataSet = new DataSet().Copy().Tables[0]. public DataTable TraerDataTable(string procedimientoAlmacenado) { return TraerDataSet(procedimientoAlmacenado).Copy(). public IDataReader TraerDataReader(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado). CrearDataAdapterSql(comandoSql).Fill(mDataSet). args).ExecuteReader().

Output) resp = par. Solo funciona con SP's que tengan // definida variables de tipo output. .Value. return resp.InputOutput || par.ExecuteReader(). return com.Direction == ParameterDirection.InputOutput || par. } // end TraerValor // Obtiene un Valor Escalar a partir de un Procedimiento Almacenado. public object TraerValorOutput(string procedimientoAlmacenado. } // end TraerDataReaderSql // Obtiene un Valor Escalar a partir de un Procedimiento Almacenado. // ejecutar el command com. // declarar variable de retorno Object resp = null. } // end TraerValor // Obtiene un Valor a partir de un Procedimiento Almacenado.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par. params Object[] args) { // asignar el string sql al command var com = Comando(procedimientoAlmacenado). // cargar los parametros del SP CargarParametros(com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.ExecuteNonQuery().Value. args). // recorrer los parametros del SP foreach (IDbDataParameter par in com.Direction == ParameterDirection.Direction == ParameterDirection. y sus parámetros. // declarar variable de retorno Object resp = null. // recorrer los parametros del SP foreach (IDbDataParameter par in com. // ejecutar el command com. para funciones escalares mas abajo se declara un metodo public object TraerValorOutput(string procedimientoAlmacenado) { // asignar el string sql al command var com = Comando(procedimientoAlmacenado). // ejecutar el command com.ExecuteNonQuery().Direction == ParameterDirection. public object TraerValorOutputSql(string comadoSql) { // asignar el string sql al command var com = ComandoSql(comadoSql).158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 var com = ComandoSql(comandoSql). return resp.Output) resp = par.ExecuteNonQuery().

return com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par. protected abstract void CargarParametros(IDbCommand comando.ExecuteScalar(). return com. } // end TraerValorEscalar // Obtiene un Valor de una funcion Escalar a partir de un Query SQL public object TraerValorEscalarSql(string comandoSql) { var com = ComandoSql(comandoSql). con Params de Entrada public Object TraerValorEscalar(string procedimientoAlmacenado. protected abstract IDbCommand ComandoSql(string comandoSql).219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 // declarar variable de retorno Object resp = null.Value. return com. } // end TraerValorEscalarSql #endregion #region "Acciones" cadena). CargarParametros(com.ExecuteScalar(). args).InputOutput || par. Object[] args). params object[] args) { var com = Comando(procedimientoAlmacenado). // recorrer los parametros del Query (uso tipico envio de varias sentencias sql en el mismo command) foreach (IDbDataParameter par in com. protected abstract IDbConnection CrearConexion(string protected abstract IDbCommand Comando(string procedimientoAlmacenado).Direction == ParameterDirection. protected abstract IDataAdapter CrearDataAdapterSql(string comandoSql). // metodo sobrecargado para autenticarse contra el motor de BBDD . params Object[] args). } // end TraerValorEscalar /// Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado.Direction == ParameterDirection. protected abstract IDataAdapter CrearDataAdapter(string procedimientoAlmacenado.Output) resp = par. public object TraerValorEscalar(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado).ExecuteScalar(). return resp. } // end TraerValor // Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado.

public int Ejecutar(string procedimientoAlmacenado.ExecuteNonQuery().Parameters[i].280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 public bool Autenticar() { if (Conexion. return true. } // end Ejecutar //Ejecuta un Procedimiento Almacenado en la base.State != ConnectionState. CargarParametros(com.ExecuteNonQuery(). i .ExecuteNonQuery(). if (par. MConexion. } // end CerrarConexion // Ejecuta un Procedimiento Almacenado en la base. public int Ejecutar(string procedimientoAlmacenado) { return Comando(procedimientoAlmacenado). return true. }// end Autenticar // cerrar conexion public void CerrarConexion() { if (Conexion.Close().InputOutput || par.Open().Count. utilizando los parámetros. var resp = com. params Object[] args) { var com = Comando(procedimientoAlmacenado).SetValue(par. i < com.Parameters. args).Direction == ParameterDirection. MPassword = vPassword. } // end Ejecutar // Ejecuta un query sql public int EjecutarSql(string comandoSql) { return ComandoSql(comandoSql).Closed) MConexion. i++) { var par = (IDbDataParameter)com.Open(). for (var i = 0.1). MConexion = CrearConexion(CadenaConexion). string vPassword) { MUsuario = vUsuario. }// end Autenticar // metodo sobrecargado para autenticarse contra el motor de BBDD public bool Autenticar(string vUsuario.Direction == ParameterDirection.Value.Open) Conexion. } // end Ejecutar #endregion .State != ConnectionState. }// end for return resp.Output) args.

protected bool EnTransaccion. EnTransaccion = true. }// end finally }// end TerminarTransaccion //Cancela la transacción activa. //Comienza una Transacción en la base en uso. } finally { MTransaccion = null. }// end finally }// end AbortarTransaccion #endregion }// end class gDatos }// end namespace . public void AbortarTransaccion() { try { MTransaccion.BeginTransaction(). }// end try finally { EnTransaccion = false.#region "Transacciones" protected IDbTransaction MTransaccion. public void IniciarTransaccion() { try { MTransaccion = Conexion. } }// end IniciarTransaccion //Confirma la transacción activa. } finally { MTransaccion = null. EnTransaccion = false.Commit(). public void TerminarTransaccion() { try { MTransaccion. EnTransaccion = false.Rollback().

sCadena."). sCadena. . Ahora nos enfocaremos en crear una capa para conectarnos a SQL Server. si llegamos a cambiar de proveedor de base de datos en algún momento.Hashtable ColComandos = new System. Lo siguiente que crearemos en una colección de hashtable para preservar por así decirlo los comandos ejecutados y accederlos con mayor velocidad si ya fue ejecutado una vez. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public override sealed string CadenaConexion { get { if (MCadenaConexion. La misma será una clase que hereda de la clase GDatos 1 using System.ToString(). El primer método que tendrá esta clase.Hashtable().Length != 0) { var sCadena = new System.Replace("<USER>". es un método sellado que sobreescibirá el de su padre.Collections.Text.").Append("data source=<SERVIDOR>. Servidor). (la primera lo pueden ver aqui). sCadena.StringBuilder("").Append("persist security info=True.Collections. Base).Append("password=<PASSWORD>. ni siquiera debemos modificar ésta clase que veremos ahora.").Replace("<PASSWORD>".Append("user id=<USER>. sCadena.Length == 0) { if (MBase. return sCadena."). sCadena. 1 static readonly System.Continuando con la segunda entrega de la programación en n-Capas. sCadena.").Replace("<SERVIDOR>". sCadena. Usuario).Append("initial catalog=<BASE>. sCadena. el unico cambio que se hará si se desea hacer esto es en una ÚNICA línea de código en una clase superior que veremos en la tercer entrega. y creará el ConnectionString y lo retornorá. Password). Para ésta clase el único uso que haremos será del namspace System.Length != 0 && MServidor. lo único que deberíamos hacer es agregar una clase semejante a ésta con la implementación especifica para éste motor. Hasta el momento solo creamos una clase abstracta que servirá de padre para las demás implementaciones (1 clase por cada fabricante de motor).Replace("<BASE>". sCadena. También se encontrará en el mismo namespace que la clase anterior AccesoDatos.

Data.Data. 1 Object[] args) 2{ 3 for (int i = 1. 18 ColComandos.Close().SqlClient.Data.DeriveParameters(com).SqlClient.SqlParameter)com.SqlClient.SqlClient.} throw new Exception("No se puede establecer la cadena de conexión en la clase SQLServer").SqlCommand(procedimientoAlmacenado.SqlClient. i < com. 8 } // end for } // end CargarParametros Luego para crear el Comando a ejecutar crearemos un método que revisará en el hashTable anteriormente creado. 19 }//end else 20 com. 7 p.SqlTransaction)MTransaccion.Contains(procedimientoAlmacenado)) com = (System.Parameters.Value = i <= args.Data.Length ? args[i . else { var con2 = new System. 17 con2.SqlClient.SqlClient.SqlCommand com. com). if (ColComandos.SqlConnection(CadenaConexion). En caso que estemos dentro de una transacción conectado.SqlClient. }// end get set { MCadenaConexion = value. si ya se lo ha hecho o no. 16 con2.Data.Transaction = (System.IDbCommand com.Open().1] : null.Add(procedimientoAlmacenado.Data. i++) 4 { 5 var p = 6 (System.SqlConnection)Conexion.CommandType. protected override System.IDbCommand Comando(string procedimientoAlmacenado) { System. que nos permitirá cargar una cantidad n de parámetros.Connection = (System.Parameters[i]. con2) { CommandType = System. }// end Comando . a los SP que invocaremos protected override void CargarParametros(System. } // end set }// end CadenaConexion Ésta es una de las caracteristicas mas llamativas y útiles.SqlCommandBuilder. necesitamos crear una segunda conexión para la lectura de los parámetros de Procedimiento almacenado.Data.StoredProcedure }. return com.SqlCommand)ColComandos[procedimientoAlmacenad o]. com = new System.Dispose(). com. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 System.Data.Data.Count. } return MCadenaConexion.Data.Data. con2.

return com.SqlClient.SelectCommand.Length != 0) CargarParametros(da.Data.SqlDataAdapter((System.IDataAdapter CrearDataAdapter(string procedimientoAlmacenado.SqlClient.Data.Data. este método es para las llamadas a Procedimientos protected override System.Data.IDbCommand ComandoSql(string comandoSql) { var com = new System. pero para ejecución de query’s SQL protected override System.SqlCommand(comandoSql. 6 Password = "". me veo obligado a crear un método extra para crear el comando si queremos ejecutar sentencias SQL directamente desde la App. args).Data. 7 }// end DatosSQLServer 8 .Como no puedo sobrecargar un método con la misma cantidad de parámetros que recibe.SqlClient.Data.Data. 5 Usuario = "".SqlDataAdapter((System.SqlTransaction)MTransaccion). } Ya en este punto si intentamos programar en capas. if (args. 4 Servidor = "". (System.SqlClient.IDbConnection CrearConexion(string 1 cadenaConexion) 2 { return new System. }// end Comando 1 2 3 4 5 Ahora crearemos la conexión a partir de la cadena de conexión protected override System.SqlComman d) Comando(procedimientoAlmacenado)).SqlClient. params Object[] args) { var da = new System.SqlClient. supongo que sabemos para que sirve un DataAdapter. y del mismo tipo. simplemente veremos como crearlo y asociarlo con el commando a su vez. protected override System.SqlConnection(cadenaConexion).SqlConnection)Conexion.SqlClient. } // end CrearDataAdapter 1 2 3 4 5 6 7 La siguiente es casi los mismo que la anterior.Data.SqlComman d) ComandoSql(comandoSql)).SqlClient. return da.Data.Data. return da.IDataAdapter CrearDataAdapterSql(string comandoSql) { var da = new System. (System. creamos 4 sobrecargas del mismo según lo que necesitemos más adelante. 1 public SqlServer() 2 { 3 Base = "". } // end CrearDataAdapterSql 1 2 3 4 5 Nada más queda crear los constructores.Data.Data.

sCadena. Servidor = servidor.Length != 0 && MServidor.StringBuilder("").Append("data source=<SERVIDOR>. se guardará * en memoria hasta que la instancia del objeto se destruya."). * En este caso.Append("initial catalog=<BASE>.9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public SqlServer(string cadenaConexion) { CadenaConexion = cadenaConexion. procediendo de igual forma que en los anteriores.").Length != 0) { var sCadena = new System. string usuario. Servidor = servidor. namespace AccesoDatos { public class SqlServer : GDatos { /* * Continuaremos con el método Comando.Hashtable ColComandos = new System. sCadena. }// end DatosSQLServer El código completo quedaría así: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 using System.").Length == 0) { if (MBase. implementaremos un mecanismo de “preservación” de los Comandos creados. string @base) { Base = @base. con el modificador Shared (compartida) que permite * persistir la misma entre creaciones de objetos */ static readonly System. }// end DatosSQLServer public SqlServer(string servidor. cada procedimiento que sea accedido. sCadena. Usuario = usuario. sCadena.Append("password=<PASSWORD>.").Text.Append("user id=<USER>.Collections. }// end DatosSQLServer public SqlServer(string servidor. Password = password. además. * para acelerar su utilización. string @base. declararemos una variable * como HashTable para la clase. Para ello.Collections. . string password) { Base = @base. public override sealed string CadenaConexion { get { if (MCadenaConexion.Hashtable(). Esto es.

Parameters.Replace("<BASE>".packet sCadena. por intentar leer sin tener asignado el * objeto Transaction correspondiente). * y su agregado en el repositorio. * la cantidad de elementos en el vector de argumentos. Usuario). i < com.Count. sCadena.Append("user id=sa. * como la ejecución de procedimientos almacenados que devuelven un valor como parámetro de salida. el cual deberá asignar cada valor * al parámetro que corresponda (considerando que. p.SqlParameter)com.Object[] Args) */ protected override void CargarParametros(System.Data.ToString().1] : null. } // end set }// end CadenaConexion /* * Agregue ahora la definición del procedimiento CargarParametros.Replace("<PASSWORD>". Dado que cabe la posibilidad de que ya estemos dentro de una transacción.Append("persist security sCadena. el comando. * Por ello.Data. return sCadena. . en el caso de SQLServer©. Por otra parte. * es necesario abrir una segunda conexión a la base de datos. Además. para obtener la definición de los parámetros * del procedimiento Almacenado (caso contrario da error.Replace("<SERVIDOR>". sCadena. } throw new Exception("No se puede establecer la cadena de conexión en la clase DatosSQLServer").38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 info=True. Password).SqlClient.IDbCommand com. Caso contrario. Servidor).Value = i <= args."). * protected override void CargarParametros(System. sCadena. sCadena.IDbCommand Com. puede no corresponder con la cantidad de parámetros.Parameters[i]. System. se procederá a la creación del mismo. se buscará primero si ya existe el comando en dicha Hashtable para retornarla * (convertida en el tipo correcto). size=4096").Length ? args[i .Data.Replace("<USER>". se decide comparar el indicador con la cantidad de argumentos recibidos. antes de asignar el valor. } return null. } // end for } // end CargarParametros /* * En el procedimiento Comando. }// end get set { MCadenaConexion = value. i++) { var p = (System. el parameter 0 * siempre corresponde al “return Value” del Procedimiento Almacenado). en algunos casos. Object[] args) { for (int i = 1. Base).

StoredProcedure }.Data.SqlClient.Data. 140 (System. 139 (System. } 155 156 157 //Finalmente.SqlClient.SqlClient.Dispose().Data.Data.SqlCommand(comandoSql.IDbCommand ComandoSql(string 135 comandoSql) 136 { 137 var com = new 138 System.SqlConnection)Conexion.CommandType. 117 com = new 118 System.IDbCommand Comando(string 105 procedimientoAlmacenado) 106 { 107 System. 129 com. con2) 119 { CommandType = 120 System.Open().SqlConnection(CadenaConexion).Data. el 158 cual aprovecha el método Comando para crear el comando necesario.SqlClient.DeriveParam 122 eters(com). 150 */ 151 protected override System.SqlClient. 141 return com. 112 else 113 { 114 var con2 = new 115 System. com).SqlClient. utilizando la cadena de 149 conexión del objeto. 126 }//end else 127 com.IDataAdapter .SqlClient. 116 con2.SqlClient.Data. 108 if (ColComandos.Close().Add(procedimientoAlmacenado. donde simplemente 147 se devuelve una nueva instancia del 148 * objeto Conexión de SqlClient.SqlCommand com. 125 ColComandos.Data. 123 con2.SqlCommandBuilder. 159 protected override System.SqlClient.SqlClient.Data. es el turno de definir CrearDataAdapter.Transaction = 130 (System. la variable es null.IDbConnection 152 CrearConexion(string cadenaConexion) 153 { return new 154 System.SqlConnection(cadenaConexion).Connection = 128 (System.Data.99 obtenido por cualquiera de los mecanismos 100 * debe recibir la conexión y la transacción 101 correspondientes (si no hay Transacción.Data.Data.SqlConnection)Conexion.Data. 131 return com.Data.Data.SqlTransaction)MTransaccion.Data.SqlClient.Contains(procedimientoAlmacenado)) 109 com = 110 (System. 142 }// end Comando 143 144 145 /* 146 * Luego implementaremos CrearConexion.SqlCommand)ColComandos[procedimientoAlmacena 111 do].SqlCommand(procedimientoAlmacenado.Data. 102 * y ese es el valor que se le pasa al objeto Command) 103 */ 104 protected override System.SqlTransaction)MTransaccion). 132 }// end Comando 133 134 protected override System. 124 con2. 121 System.

}// end DatosSQLServer }// end class DatosSQLServer }// end namespace . el cual aprovecha el método Comando para crear el comando necesario. string password) { Base = @base. string usuario. Password = "".SqlDataAdapter((System.Length != 0) CargarParametros(da.SqlComm and) ComandoSql(comandoSql)). params Object[] args) { var da = new System. Servidor = "". args).SqlClient. } // end CrearDataAdapter //Finalmente. uno que reciba como argumentos los valores de Nombre del Servidor * y de base de datos que son necesarios para acceder a datos.Data.SqlDataAdapter((System. string @base) { Base = @base.SelectCommand. Usuario = "". if (args.SqlClient.CrearDataAdapter(string procedimientoAlmacenado. es el turno de definir CrearDataAdapter. }// end DatosSQLServer public SqlServer(string cadenaConexion) { CadenaConexion = cadenaConexion. Password = password. return da. } // end CrearDataAdapterSql /* * Definiremos dos constructores especializados. }// end DatosSQLServer public SqlServer(string servidor. y otro que admita directamente la cadena de conexión completa.Data.IDataAdapter CrearDataAdapterSql(string comandoSql) { var da = new System. Servidor = servidor. * Los constructores se definen como procedimientos 160 públicos de nombre New. Servidor = servidor.SqlClient.SqlComm and) Comando(procedimientoAlmacenado)).SqlClient.Data. Usuario = usuario.Data.Data. protected override System. return da. 161 */ 162 public SqlServer() 163 { Base = "". string @base. }// end DatosSQLServer public SqlServer(string servidor.

También pertenecerá al namespace . También daremos por terminada la capa de Acceso a Datos. en éste caso motores de base de datos. El motivo de su longitud es por que es una clase que se utiliza como medio para crear la flexibilidad y portabilidad de fuentes de datos.Esta es la tercer entrega. probablemente será la más corta pero no la última aún. entonces así no mezclamos el código y será más fácil seguirlo posteriormente.

} //fin inicializa sesion public static void FinalizarSesion() { GDatos. solamente cambiariamos una linea de código. string usuario. OleDB para conexiones genéricas. No les parece grandioso que solo deban tocar parte de una línea de código para portar la App a cualquier otro motor de Base de Datos? namespace AccesoDatos { 1 public class Conexion { public static GDatos GDatos. como ya dije en los artículos anteriores hemos dado por terminado la capa de Acceso a Datos. Oracle u otro motor que codifiquemos. Aquí es donde diremos como debe procesarse la información. sea SqlServer. string password) { GDatos = new SqlServer(nombreServidor. Creo que ya van captando el rumbo de esto no? si crearamos otra clase por ejemplo Oracle. la capa de Negocios.Autenticar(). string baseDatos. baseDatos.CerrarConexion(). Primeramente crearemos una tabla realmente simple. } //fin FinalizaSesion }//end class util }//end namespace 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 En la siguiente entrega veremos como crear la capa de Negocio y vincularla a estas capas de Acceso a Datos. Podemos hacerlo con ODBC. 1 CREATE TABLE [dbo].AccesoDatos.cs. Lo llamo conexión por que es la clase con las otras capas interactuaran en modo directo. public static bool IniciarSesion(string nombreServidor. password).cs o MySQL. Para ello creamos un objeto estático de la clase GDatos que instanciará de la clase SqlServer. donde el objeto GDatos del tipo GDatos.[Cliente]( . compuesta por 3 campos llamada Cliente. pero reflejará claramente como se usa ésta capa en casos más complejos. return GDatos. usuario. En la cuarta entrega veremos una capa nueva. Para este caso no voy a crear una estructura compleja de BBDD ya que el código de C# ya lleva bastante.

1 public short IdCliente { get. END CREATE PROCEDURE [dbo]. set. @nombre varchar(50). Dentro del mismo proyecto de biblioteca de clases. END CREATE PROCEDURE [dbo].[slcCliente] AS BEGIN select * from Cliente. [DescripcionCliente] [varchar](50) NOT NULL. Creo conveniente crear una carpeta en la raíz del proyecto llamada Orm. ALLOW_PAGE_LOCKS ON [PRIMARY] ) ON [PRIMARY] = ON) Preferentemente trabajaremos con procedimientos almacenados como Zeus manda. Del lado de la aplicación utilizaremos el mapeo ORM (doy por sabido que conocen que esto. @sigla). crearemos sus setters y getters. Al crear esta carpeta. al menos hasta donde vamos a utilizar. IGNORE_DUP_KEY = OFF. } .cs. es lo que venía escribiendo en las primeras partes del tutorial. y crear clases dentro de ella también se creará un nuevo nivel de espacios de nombres: AccesoDatos. ya que es una de la prácticas más utilizadas y probadas que ahorran código y ayudan a cumplir con varios conceptos de la OOP.[slcCliente] AS BEGIN select * from Cliente.[insCliente] @id as int. STATISTICS_NORECOMPUTE = OFF. sabrán disculparme :S). @nombre. En ella crearemos 3 variables. 1 2 3 4 5 6 7 8 1 2 3 4 5 1 2 3 4 5 CREATE PROCEDURE [dbo]. CONSTRAINT [PK_Cliente] PRIMARY KEY CLUSTERED ( [IdCliente] ASC )WITH (PAD_INDEX = OFF. justamente para seguir la convención (trato de seguirlas todas. derrepente se me escapa alguna. Estos SP. se pueden considerar parte de la capa de negocios. Para crear. A esta la llamaremos Cliente. ALLOW_ROW_LOCKS = ON. sino pueden investigarlo aquí). no precisamente siempre tiene que estar en la aplicación.2 3 4 5 6 7 8 9 [IdCliente] [tinyint] NOT NULL. listar y buscar tendremos 3 distintos procedimientos. y así una clase por cada tabla que tengamos en nuestra BBDD. END Con esto ya tenemos creada la estructura de la base de datos completa. que estamos teniendo en nuestra solución (hasta ahora no hemos creado ningún tipo de interfaz de usuario).Orm. Al mismo tiempo de crear estos. set. @sigla varchar(15) AS BEGIN insert into Cliente values(@id. } 2 public string Descripcion { get. que representarán los atributos de Cliente (mapeando los campos de la tabla). [Siglas] [varchar](15) NULL.

} return null.IdCliente. set.Descripcion. if (dt. set. cliente. un registro de cliente. } public Cliente Listar(int idCliente) .GDatos. Pueden crear todos los que quieran. } public DataTable Listar() { return Conexion. cliente. } public DataTable Listar() { return Conexion. return cliente.Ejecutar("insCliente". en la BBDD. que correspondan a la clase cliente. } En esta clase.Rows[0][0]).TraerDataTable("slcClienteById".Orm { public class Cliente { // lista de atributos con setters y getters public short IdCliente { get. 3). 3). } Ahora es momento de utilizar el potencial del código que hemos venido escribiendo.Ejecutar("insCliente".GDatos.Data.ToInt16(dt.3 public string Sigla { get.ToString(dt. idCliente). cliente. set. sin importar que ésta clase quede muy larga.IdCliente.ToString(dt. que sea aquí donde corresponde una acción en sí.TraerDataTable("slcCliente"). y otro para listar todos. cliente.Rows. uno para dar de Alta. cliente. La clase completa quedaría así: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using System. otro para buscar.Count > 0) { cliente.Sigla.Sigla = Convert. } public string Descripcion { get. deben crear todos los métodos. set.Rows[0] cliente. simplemente deben encargarse. Como ven estamos aplicando sobrecarga de métodos en ésta clase (no lo confundan con polimorfismo).GDatos. cliente.Descripcion.Descripcion = Convert. namespace AccesoDatos. } // métodos public void Crear(Cliente cliente) { Conexion. } public Cliente Listar(int idCliente) { var cliente = new Cliente(). veanlo en acción en los 3 métodos que crearemos. } public string Sigla { get. DataTable dt = Conexion.Sigla. cliente.IdCliente = Convert.GDatos. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [1]). { Conexion.TraerDataTable("slcCliente").Rows[0][2]).GDatos. using System.

Descripcion = Convert.GDatos.ToString(dt. utilizaremos todo lo que hemos visto hasta ahora aplicados a una interfaz de usuario. 24 25 if (dt.ToInt16(dt.ToString(dt. en lo posible. 30 cliente. El primer ejemplo será Desktop.Rows[0][2]). 33 } 34 return null. utilizares todas las clases hechas ya incluida ésta en una aplicación WinForm y otra WebForm. 31 32 return cliente.Count > 0) 26 { 27 cliente. lo veremos implementado en winForm como en webForm. crearemos un formulario con una apariencia semejante al que ven en la imagen. 35 } 36 } } { Con ésto vemos como se implementa la capa de negocios.Rows[0] 29 [1]). . idCliente). En la siguiente entrega veremos ya la capa de Presentación. y como lo prometí. sino que es portable a distintas plataformas de ejecución. verán que no sólo es portable a cualquier proveedor de datos. Con ésta entrega cumpliremos con la capa de Presentación. 22 DataTable dt = 23 Conexion. 28 cliente.Rows.IdCliente = Convert.var cliente = new Cliente().Rows[0][0]).Sigla = Convert.TraerDataTable("slcClienteById".

le seteamos sus atributos. el boton crear mandará a la BBDD los datos de la caja.0.Show(String. a partir de los valores de la caja de texto. un sistema real no lo harán así. uno será una aplicación Windows Form con Visual C#. "sa". el botón conectar emula el comportamiento de una pantalla de login. 1 var cliente = new Cliente(). Listar rellenará la grilla y Buscar By Id se encargará de devolvernos un registro a partir de lo que carguemos en la caja de Id.Message). MessageBox. Otra implementación interesante sería agregarle un identity a la tabla cliente. "Queryable". "Se conecto exitosamente")).1".Evidentemente.Format("{0}". no se mantiene conectada la aplicación. } catch (Exception ex) { MessageBox.IniciarSesion("127.0. 2 try 3 { . recuerden que es conveniente armarlo dinamicamente con una pantalla de login especialmente el usuario y el pass. sino solo sus valores de conexión en memoria. El código que pondremos en el botón conectar es como sigue. Para esto crearemos 2 proyectos más dentro de nuestra solución. e invocamos el método crear enviandole el nuevo objeto cliente. 1 2 3 4 5 6 7 8 9 try { Conexion. y la otra un sitio Web. } Para crear un nuevo cliente instanciamos un objeto cliente del tipo Cliente. Con esto logramos armar toda la capa de Acceso a Datos.Show(ex. pero para el ejemplo ya servirá esto. "123").

} Luego de esto escribimos el código de listado de todos los clientes. siempre nos devolverá un sólo registro.Text = cliente.IdCliente = Convert.Listar(Convert.DataSource = cliente. cliente.Text).ToInt16(txtIdCliente.Format("{0}".Show(String.ToInt16(txtIdCliente.4 5 6 7 8 9 10 11 12 13 14 cliente.Sigla. "Se creo exitosamente")). MessageBox. txtSiglaCliente.Text. DataSet o DataReader.Message).Sigla = txtSiglaCliente.Text = cliente.Descripcion.Forms.Message). lo podemos crear como un objeto Cliente. 3 using AccesoDatos.Text. cliente. y lo cargamos en la grilla.Format("{0}".Show(String.Show(ex. try { cliente = cliente. try { grilla. cuando vamos a devolver muchos registros no podemos utilizar un montón de instancias de Cliente. Se dan cuenta que necesitamos muy pocas lineas de código en la capa de Presentación.Listar().Text)). Cómo sabemos que si buscamos por la PK.Crear(cliente). if (cliente != null) { txtDescripcionCliente. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var cliente = new Cliente(). } El código completo quedaría así: 1 using System. Si se dan cuenta.Show(ex. 1 2 3 4 5 6 7 8 9 var cliente = new Cliente(). } else { MessageBox.Descripcion = txtDescripcionCliente. sino simplemente devolvemos un DataTable. } } catch (Exception ex) { MessageBox. } catch (Exception ex) { MessageBox.Windows. . } catch (Exception ex) { MessageBox. cliente. y que no tiene una dependencia de la BBDD?. } Finalmente el código de búsqueda quedaría algo asi.Message).Show(ex. 2 using System. "No existia el cliente buscado")).

1". EventArgs e) { var cliente = new Cliente(). EventArgs e) { . } } private void btnCrear_Click(object sender. EventArgs e) { try { Conexion. } catch (Exception ex) { MessageBox.Crear(cliente).Format("{0}".Text. cliente. try { grilla. namespace Test { public partial class Form1 : Form { public Form1() { InitializeComponent().Text. "Se creo exitosamente")).Orm. } } private void btnBuscarById_Click(object sender. MessageBox. } catch (Exception ex) { MessageBox. "Se conecto exitosamente")).ToInt16(txtIdCliente.4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 using AccesoDatos.Show(String.Show(ex.0.Show(ex.Format("{0}".Message). MessageBox.Show(ex.Message).Text).Listar().DataSource = cliente.IniciarSesion("127. cliente. "sa". "Queryable". } private void btnConectar_Click(object sender. "***").Descripcion = txtDescripcionCliente.IdCliente = Convert. } } private void btnListar_Click(object sender. try { cliente. EventArgs e) { var cliente = new Cliente().0. cliente.Sigla = txtSiglaCliente. } catch (Exception ex) { MessageBox.Message).Show(String.

Sigla. 80 } } } } Con respecto a la parte web. } 76 } 77 catch (Exception ex) 78 { 79 MessageBox. el código ASP.0 Transitional//EN" "http://www.Text = cliente.aspx.Message).dtd"> <html xmlns="http://www. 66 67 if (cliente != null) 68 { 69 txtDescripcionCliente. veamoslo.Show(String. "No existia 75 el cliente buscado")).Format("{0}".cs" Inherits="Cliente" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. y el c#.var cliente = new Cliente().org/TR/xhtml1/DTD/xhtml1-transitional. 72 } 73 else 74 { MessageBox.ToInt16(txtIdCliente. try { cliente = 65 cliente.Show(ex. se los dejo completamente de una: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Cliente.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="frmCliente" runat="server"> <div> <asp:Label ID="Id" runat="server" Text="Id: "></asp:Label> <asp:TextBox ID="txtIdCliente" runat="server"></asp:TextBox> <br /> <asp:Label ID="Label1" runat="server" Text="Descripcion: "></asp:Label> <asp:TextBox ID="txtDescripcionCliente" runat="server"></asp:TextBox> <asp:TextBox ID="txtuser" runat="server">sa</asp:TextBox> <br /> <asp:Label ID="Label2" runat="server" Text="Siglas: "></asp:Label> <asp:TextBox ID="txtSiglasCliente" runat="server"></asp:TextBox> <br /> <hr style="margin-top: 0px. margin-bottom: 0px" /> <asp:GridView ID="grilla" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None"> <AlternatingRowStyle BackColor="White" /> .net.Descripcion.Text)).Text = 70 cliente.Listar(Convert. tendremos 2 partes. 71 txtSiglaCliente.w3.

Message).0. EventArgs e) 12 { 13 try 14 { 15 lblEstado.34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <EditRowStyle BackColor="#2461BF" /> <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" /> <RowStyle BackColor="#EFF3FB" /> <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" /> <SortedAscendingCellStyle BackColor="#F5F7FB" /> <SortedAscendingHeaderStyle BackColor="#6D95E1" /> <SortedDescendingCellStyle BackColor="#E9EBEF" /> <SortedDescendingHeaderStyle BackColor="#4870BE" /> </asp:GridView> <asp:Label ID="Label3" runat="server" Text="Estado:"></asp:Label> <asp:Label ID="lblEstado" runat="server"></asp:Label> <br /> <asp:Button ID="btnConectar" runat="server" Text="Conectar" onclick="btnConectar_Click" /> <asp:Button ID="btnCrear" runat="server" onclick="btnCrear_Click" Text="Crear" /> <asp:Button ID="btnListar" runat="server" onclick="btnListar_Click" Text="Listar" /> <asp:Button ID="btnListarById" runat="server" onclick="btnListarById_Click" Text="Listar By Id" /> <br /> </div> </form> </body> </html> 1 using System.IniciarSesion("127.Text = String.Text = "".Format(ex. "***").Conexion. 4 5 public partial class Cliente : System. 16 AccesoDatos.UI. "sa". 24 } 25 } .Web. 17 "Queryable". 20 } 21 catch (Exception ex) 22 { 23 lblEstado. "Se conecto 19 exitosamente"). EventArgs e) 8 { 9 10 } 11 protected void btnConectar_Click(object sender.Text = String.Format("{0}".1". 18 lblEstado.0.Page 6 { 7 protected void Page_Load(object sender. 2 3 // using AccesoDatos.

EventArgs e) { var cliente = new AccesoDatos.Text = ex.Format("{0}".Text = cliente.Format("{0}".Text = ex.Listar().Format("{0}". grilla.Text.Cliente().Message.Text).DataSource = cliente.Crear(cliente).Sigla. cliente. try { cliente.Cliente().ToInt16(txtIdCliente. . "No existia el 71 cliente buscado"). 54 } 55 } 56 protected void btnListarById_Click(object sender. 68 } 69 else 70 { lblEstado. serán bienvenidas de hecho éste código lo creo un MVP de Microsoft y yo le agregue algunas funcionalidades.Descripcion = txtDescripcionCliente. EventArgs e) 57 { 58 var cliente = new AccesoDatos. 63 64 if (cliente != null) 65 { 66 txtDescripcionCliente.Orm.. "Se creo 49 exitosamente"). lblEstado. 67 txtSiglasCliente.IdCliente = Convert.26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 cliente. try { grilla. } } } protected void btnListar_Click(object sender. 50 } 51 catch (Exception ex) 52 { 53 lblEstado.Descripcion. cliente.Text = cliente. } catch (Exception ex) { lblEstado. } } protected void btnCrear_Click(object sender.Text = String. 59 try 60 { 61 cliente = 62 cliente.Text.Cliente(). } 72 } 73 catch (Exception ex) 74 { 75 lblEstado.Text = String.Orm.Message. 48 lblEstado.Text = ex.Orm.Listar(Convert. EventArgs e) { var cliente = new AccesoDatos.DataBind(). Si pueden agregar/aportar mejoras y funcionalidad a estas clases.Message.Sigla = txtSiglasCliente.ToInt16(txtIdCliente.Text)).Text = String. "Se listo exitosamentë").

Sign up to vote on this title
UsefulNot useful