Programar en C#
Programar en C#
Programar en C#
Este es un tema polmico del que se habla mucho y nada, digo que se habla mucho porque al buscar algo de informacin en Internet, uno se da cuenta, que est plagado de sitios donde preguntan cmo aplicar programacin en 3 capas, o NCapas, pero en muy pocos lugares se responde con algo cierto y concreto, la mayora hacen referencia a libros gordos que tardaras en leer semanas (no estoy en contra de la lectura, es un proceso largo nada ms y casi todos buscamos aprenderlo un poco ms rpido). Este artculo tambin 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 centmetros La primera gran confusin que noto, es que la mayora no sabe diferenciar entre los conceptos 1. Arquitectura de 3 capas: se basa ms bien en cmo ser construido el entorno, una manera de decirlo en romper el clsico 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 programacin en capas. Se acerca ms a un concepto fsico. 2. Programacin en 3 (n) capas: este es el tema en cuestin y estira ms hacia un concepto lgico. En cmo partimos, agrupamos, clasificamos, optimizamos nuestro cdigo. 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 aplicacin en s y en la BBDD), y Capa de Presentacin (es ms bien lo que el usuario percibe, su interfaz grfica 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 separacin en capas quiere decir que podemos cambiar de proveedor de base de datos, y no necesitaremos reescribir toda la aplicacin de vuelta, sino solamente esa pequea capa y reutilizaramos la interfaz y las reglas de negocios, o tambin podemos mantener las reglas de negocios y el motor de base de datos,
y fcilmente cambiarnos de una interfaz WinForm a WebForm, siempre la ms dura de cambiar en la de negocios ya que afecta en un nivel mnimo a las otras 2 capas. Creo que ya es suficiente teora de momento y podemos comenzar con la accin, el cdigo que voy a ir escribiendo lo har en Visual Studio 2010 por que es la que tengo instalada ahora mismo en la mquina, pero funciona desde la versin 2005 con el framework 2.0 en adelante, ya que ADO.Net no ha sufrido grandes cambios desde esa versin, as que ustedes lo pueden ir creando en la versin del IDE o framework que ms gusten. Primeramente vamos a crear una solucin con un proyecto deBiblioteca de Clases, el mismo tendr 3 clases principales para representar la capa de Acceso a Datos, la primera llamaremosGDatos.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 cdigo con regiones para una mayor comodidad en la lectura del mismo, la primer regin es la deDeclaracin de Variables en la misma creamos las variables o atributos para la conexion a la BBDD, ms un objeto de interfaz de conexin para que sea implementada de manera especfica 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 artculo. Esta es una clase que obligatoriamente debe ser hereda por otra. El nivel de acceso por eso estn 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 "Declaracin de Variables" protected protected protected protected protected protected string MServidor = ""; string MBase = ""; string MUsuario = ""; string MPassword = ""; string MCadenaConexion = ""; IDbConnection MConexion;
#endregion
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
#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 conexin completa a la base. public abstract string CadenaConexion { get; set; } #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(); datos return MConexion; } // end get } // end Conexion #endregion
Creamos ahora los mtodos 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
//Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parmetros. public DataTable TraerDataTable(string procedimientoAlmacenado, params Object[] args) { return TraerDataSet(procedimientoAlmacenado, args).Tables[0].Copy(); } // end TraerDataTa //Obtiene un DataTable a partir de un Query SQL public DataTable TraerDataTableSql(string comandoSql) { return TraerDataSetSql(comandoSql).Tables[0].Copy(); } // end TraerDataTableSql // Obtiene un DataReader a partir de un Procedimiento Almacenado. public IDataReader TraerDataReader(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado); return com.ExecuteReader(); } // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado y sus parmetros. public IDataReader TraerDataReader(string procedimientoAlmacenado, params object[] args) { var com = Comando(procedimientoAlmacenado); CargarParametros(com, args); return com.ExecuteReader(); } // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado. public IDataReader TraerDataReaderSql(string comandoSql) { var com = ComandoSql(comandoSql); return com.ExecuteReader(); } // end TraerDataReaderSql
// Obtiene un Valor Escalar a partir de un Procedimiento Almacenado. Solo funciona con SP's // definida variables de tipo output, para funciones escalares mas abajo se declara un meto
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 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
public object TraerValorOutput(string procedimientoAlmacenado) { // asignar el string sql al command var com = Comando(procedimientoAlmacenado); // ejecutar el command com.ExecuteNonQuery(); // declarar variable de retorno Object resp = null; // recorrer los parametros del SP foreach (IDbDataParameter par in com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection.InputOutput ParameterDirection.Output) resp = par.Value; return resp; } // end TraerValor // Obtiene un Valor a partir de un Procedimiento Almacenado, y sus parmetros. public object TraerValorOutput(string procedimientoAlmacenado, params Object[] args) { // asignar el string sql al command var com = Comando(procedimientoAlmacenado); // cargar los parametros del SP CargarParametros(com, args); // ejecutar el command com.ExecuteNonQuery(); // declarar variable de retorno Object resp = null; // recorrer los parametros del SP foreach (IDbDataParameter par in com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection.InputOutput ParameterDirection.Output) resp = par.Value; return resp; } // end TraerValor // Obtiene un Valor Escalar a partir de un Procedimiento Almacenado. public object TraerValorOutputSql(string comadoSql) { // asignar el string sql al command var com = ComandoSql(comadoSql); // ejecutar el command com.ExecuteNonQuery(); // declarar variable de retorno Object resp = null;
||
||
// recorrer los parametros del Query (uso tipico envio de varias sentencias sql en foreach (IDbDataParameter par in com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection.InputOutput || ParameterDirection.Output) resp = par.Value; return resp; } // end TraerValor // Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado. public object TraerValorEscalar(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado); return com.ExecuteScalar(); } // end TraerValorEscalar
/// Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado, con Pa public Object TraerValorEscalar(string procedimientoAlmacenado, params object[] args) {
131 132 133 134 135 136 137 138 139 140
var com = Comando(procedimientoAlmacenado); CargarParametros(com, args); return com.ExecuteScalar(); } // end TraerValorEscalar // Obtiene un Valor de una funcion Escalar a partir de un Query SQL public object TraerValorEscalarSql(string comandoSql) { var com = ComandoSql(comandoSql); return com.ExecuteScalar(); } // end TraerValorEscalarSql #endregion
El siguiente bloque es para ejecutar procesos que no devuelven valores, al inicio tendremos varios mtodos abstractos, para que las clases derivadas estn obligadas a implementarlas a su manera, en un modo especifico, ya que los objetos connection, command, dataadapter, son muy especficos y deben ser implementados por cada 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 34 35 36 37 38 39 40 41 42 43 44 45 46 #region "Acciones" protected protected protected protected protected protected abstract abstract abstract abstract abstract abstract
IDbConnection CrearConexion(string cadena); IDbCommand Comando(string procedimientoAlmacenado); IDbCommand ComandoSql(string comandoSql); IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, params Obje IDataAdapter CrearDataAdapterSql(string comandoSql); void CargarParametros(IDbCommand comando, Object[] args);
// metodo sobrecargado para autenticarse contra el motor de BBDD public bool Autenticar() { if (Conexion.State != ConnectionState.Open) Conexion.Open(); return true; }// end Autenticar // metodo sobrecargado para autenticarse contra el motor de BBDD public bool Autenticar(string vUsuario, string vPassword) { MUsuario = vUsuario; MPassword = vPassword; MConexion = CrearConexion(CadenaConexion); MConexion.Open(); return true; }// end Autenticar // cerrar conexion public void CerrarConexion() { if (Conexion.State != ConnectionState.Closed) MConexion.Close(); } // end CerrarConexion // Ejecuta un Procedimiento Almacenado en la base. public int Ejecutar(string procedimientoAlmacenado) { return Comando(procedimientoAlmacenado).ExecuteNonQuery(); } // end Ejecutar // Ejecuta un query sql public int EjecutarSql(string comandoSql) { return ComandoSql(comandoSql).ExecuteNonQuery(); } // end Ejecutar
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
//Ejecuta un Procedimiento Almacenado en la base, utilizando los parmetros. public int Ejecutar(string procedimientoAlmacenado, params Object[] args) { var com = Comando(procedimientoAlmacenado); CargarParametros(com, args); var resp = com.ExecuteNonQuery(); for (var i = 0; i < com.Parameters.Count; i++) { var par = (IDbDataParameter)com.Parameters[i]; if (par.Direction == ParameterDirection.InputOutput ParameterDirection.Output) args.SetValue(par.Value, i - 1); }// end for return resp; } // end Ejecutar #endregion
||
Ahora bien, no podemos olvidarnos de la seccin transaccional, no se utiliza normalmente en todos lados desde la aplicacin, pero en procesos dependientes es necesario, as que si necesitamos usarlo, 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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #region "Transacciones" protected IDbTransaction MTransaccion; protected bool EnTransaccion; //Comienza una Transaccin en la base en uso. public void IniciarTransaccion() { try { MTransaccion = Conexion.BeginTransaction(); EnTransaccion = true; }// end try finally { EnTransaccion = false; } }// end IniciarTransaccion //Confirma la transaccin activa. public void TerminarTransaccion() { try { MTransaccion.Commit(); } finally { MTransaccion = null; EnTransaccion = false; }// end finally }// end TerminarTransaccion //Cancela la transaccin activa. public void AbortarTransaccion() { try { MTransaccion.Rollback(); } finally { MTransaccion = null; EnTransaccion = false; }// end finally }// end AbortarTransaccion
44 #endregion
64 65 66 67 68 69 70 71 datos 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 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
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 i return MConexion; } // end get } // end Conexion #endregion #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 parmetros. 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; } // end TraerDataSetSql
// Obtiene un DataTable a partir de un Procedimiento Almacenado. public DataTable TraerDataTable(string procedimientoAlmacenado) { return TraerDataSet(procedimientoAlmacenado).Tables[0].Copy(); } // end TraerData
//Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parmetros. public DataTable TraerDataTable(string procedimientoAlmacenado, params Object[] arg { return TraerDataSet(procedimientoAlmacenado, args).Tables[0].Copy(); } // end Tra //Obtiene un DataTable a partir de un Query SQL public DataTable TraerDataTableSql(string comandoSql) { return TraerDataSetSql(comandoSql).Tables[0].Copy(); } // end TraerDataTableSql // Obtiene un DataReader a partir de un Procedimiento Almacenado. public IDataReader TraerDataReader(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado); return com.ExecuteReader(); } // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado y sus parmetros. public IDataReader TraerDataReader(string procedimientoAlmacenado, params object[] { var com = Comando(procedimientoAlmacenado); CargarParametros(com, args); return com.ExecuteReader();
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 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
} // end TraerDataReader // Obtiene un DataReader a partir de un Procedimiento Almacenado. public IDataReader TraerDataReaderSql(string comandoSql) { var com = ComandoSql(comandoSql); return com.ExecuteReader(); } // end TraerDataReaderSql
// Obtiene un Valor Escalar a partir de un Procedimiento Almacenado. Solo funciona // definida variables de tipo output, para funciones escalares mas abajo se declara public object TraerValorOutput(string procedimientoAlmacenado) { // asignar el string sql al command var com = Comando(procedimientoAlmacenado); // ejecutar el command com.ExecuteNonQuery(); // declarar variable de retorno Object resp = null;
// recorrer los parametros del SP foreach (IDbDataParameter par in com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection.InputOutput || par.Direction == Par resp = par.Value; return resp; } // end TraerValor
// Obtiene un Valor a partir de un Procedimiento Almacenado, y sus parmetros. public object TraerValorOutput(string procedimientoAlmacenado, params Object[] args { // asignar el string sql al command var com = Comando(procedimientoAlmacenado); // cargar los parametros del SP CargarParametros(com, args); // ejecutar el command com.ExecuteNonQuery(); // declarar variable de retorno Object resp = null;
// recorrer los parametros del SP foreach (IDbDataParameter par in com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection.InputOutput || par.Direction == Par resp = par.Value; return resp; } // end TraerValor // Obtiene un Valor Escalar a partir de un Procedimiento Almacenado. public object TraerValorOutputSql(string comadoSql) { // asignar el string sql al command var com = ComandoSql(comadoSql); // ejecutar el command com.ExecuteNonQuery(); // declarar variable de retorno Object resp = null;
// recorrer los parametros del Query (uso tipico envio de varias sentencias sql foreach (IDbDataParameter par in com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection.InputOutput || par.Direction == Par resp = par.Value; return resp; } // end TraerValor
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 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
// Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado. public object TraerValorEscalar(string procedimientoAlmacenado) { var com = Comando(procedimientoAlmacenado); return com.ExecuteScalar(); } // end TraerValorEscalar
/// Obtiene un Valor de una funcion Escalar a partir de un Procedimiento Almacenado public Object TraerValorEscalar(string procedimientoAlmacenado, params object[] arg { var com = Comando(procedimientoAlmacenado); CargarParametros(com, args); return com.ExecuteScalar(); } // end TraerValorEscalar // Obtiene un Valor de una funcion Escalar a partir de un Query SQL public object TraerValorEscalarSql(string comandoSql) { var com = ComandoSql(comandoSql); return com.ExecuteScalar(); } // end TraerValorEscalarSql #endregion #region "Acciones" protected protected protected protected protected protected abstract abstract abstract abstract abstract abstract
IDbConnection CrearConexion(string cadena); IDbCommand Comando(string procedimientoAlmacenado); IDbCommand ComandoSql(string comandoSql); IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, pa IDataAdapter CrearDataAdapterSql(string comandoSql); void CargarParametros(IDbCommand comando, Object[] args);
// metodo sobrecargado para autenticarse contra el motor de BBDD public bool Autenticar() { if (Conexion.State != ConnectionState.Open) Conexion.Open(); return true; }// end Autenticar // metodo sobrecargado para autenticarse contra el motor de BBDD public bool Autenticar(string vUsuario, string vPassword) { MUsuario = vUsuario; MPassword = vPassword; MConexion = CrearConexion(CadenaConexion); MConexion.Open(); return true; }// end Autenticar // cerrar conexion public void CerrarConexion() { if (Conexion.State != ConnectionState.Closed) MConexion.Close(); } // end CerrarConexion // Ejecuta un Procedimiento Almacenado en la base. public int Ejecutar(string procedimientoAlmacenado) { return Comando(procedimientoAlmacenado).ExecuteNonQuery(); } // end Ejecutar // Ejecuta un query sql
268 269 270 271 272 273 274 275 276 277 278 279 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
//Ejecuta un Procedimiento Almacenado en la base, utilizando los parmetros. public int Ejecutar(string procedimientoAlmacenado, params Object[] args) { var com = Comando(procedimientoAlmacenado); CargarParametros(com, args); var resp = com.ExecuteNonQuery(); for (var i = 0; i < com.Parameters.Count; i++) { var par = (IDbDataParameter)com.Parameters[i]; if (par.Direction == ParameterDirection.InputOutput || par.Direction == Par args.SetValue(par.Value, i - 1); }// end for return resp; } // end Ejecutar #endregion #region "Transacciones" protected IDbTransaction MTransaccion; protected bool EnTransaccion; //Comienza una Transaccin en la base en uso. public void IniciarTransaccion() { try { MTransaccion = Conexion.BeginTransaction(); EnTransaccion = true; }// end try finally { EnTransaccion = false; } }// end IniciarTransaccion //Confirma la transaccin activa. public void TerminarTransaccion() { try { MTransaccion.Commit(); } finally { MTransaccion = null; EnTransaccion = false; }// end finally }// end TerminarTransaccion //Cancela la transaccin activa. public void AbortarTransaccion() { try { MTransaccion.Rollback(); } finally { MTransaccion = null; EnTransaccion = false; }// end finally }// end AbortarTransaccion #endregion }// end class gDatos
Nota: visto que el post, se est quedando muy largo, lo separar en partes e ir agrengando de a poco las otras clases y luego las otras capas lgicas.
Continuando con la segunda entrega de la programacin en n-Capas, (la primera lo pueden ver aqui). Hasta el momento solo creamos una clase abstracta que servir de padre para las dems implementaciones (1 clase por cada fabricante de motor). Ahora nos enfocaremos en crear una capa para conectarnos a SQL Server, si llegamos a cambiar de proveedor de base de datos en algn momento, lo nico que deberamos hacer es agregar una clase semejante a sta con la implementacin especifica para ste motor, ni siquiera debemos modificar sta clase que veremos ahora, el unico cambio que se har si se desea hacer esto es en una NICA lnea de cdigo en una clase superior que veremos en la tercer entrega. Para sta clase el nico uso que haremos ser del namspace System. Tambin se encontrar en el mismo namespace que la clase anteriorAccesoDatos. La misma ser una clase que hereda de la clase GDatos
1 using System;
Lo siguiente que crearemos en una coleccin de hashtable parapreservar por as decirlo los comandos ejecutados y accederlos conmayor velocidad si ya fue ejecutado una vez.
1 static readonly System.Collections.Hashtable ColComandos = new System.Collections.Hashtable();
El primer mtodo que tendr esta clase, es un mtodo sellado quesobreescibir el de su padre, y crear el ConnectionString y lo retornor.
1 public override sealed string CadenaConexion 2 { 3 get 4 { if (MCadenaConexion.Length == 0) 5 6 { if (MBase.Length != 0 && MServidor.Length != 0) 7 8 { 9 var sCadena = new System.Text.StringBuilder(""); 10 sCadena.Append("data source=;"); 11 sCadena.Append("initial catalog=;"); 12 sCadena.Append("user id=;"); 13 sCadena.Append("password=;"); 14 sCadena.Append("persist security info=True;"); 15 sCadena.Replace("", Servidor); sCadena.Replace("", Base); 16 17 sCadena.Replace("", Usuario); 18 sCadena.Replace("", Password); 19 return sCadena.ToString(); 20 21 }
throw new Exception("No se puede establecer la cadena de conexin 22 23 SQLServer"); 24 } return MCadenaConexion; 25 26 }// end get 27 set 28 { MCadenaConexion = value; } // end set }// end CadenaConexion
sta es una de las caracteristicas mas llamativas y tiles, que nos permitir cargar una cantidad n de parmetros, a los SP que invocaremos
1 protected override void 2 args) 3 { for (int i = 1; 4 5 { 6 var p = p.Value 7 8 } } CargarParametros(System.Data.IDbCommand com, Object[] i < com.Parameters.Count; i++) (System.Data.SqlClient.SqlParameter)com.Parameters[i]; = i <= args.Length ? args[i - 1] ?? DBNull.Value : null;
Luego para crear el Comando a ejecutar crearemos un mtodo que revisar en el hashTable anteriormente creado, si ya se lo ha hecho o no. En caso que estemos dentro de una transaccin conectado, necesitamos crear una segunda conexin para la lectura de los parmetros de Procedimiento almacenado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 protected override System.Data.IDbCommand Comando(string procedimientoAlmacenado) { System.Data.SqlClient.SqlCommand com; if (ColComandos.Contains(procedimientoAlmacenado)) com = (System.Data.SqlClient.SqlCommand)ColComandos[procedimientoAlmacenado]; else { var con2 = new System.Data.SqlClient.SqlConnection(CadenaConexion); con2.Open(); com = new System.Data.SqlClient.SqlCommand(procedimientoAlmacenado, con2) { CommandType = System.Data.CommandType.StoredProcedure }; System.Data.SqlClient.SqlCommandBuilder.DeriveParameters(com); con2.Close(); con2.Dispose(); ColComandos.Add(procedimientoAlmacenado, com); }//end else com.Connection = (System.Data.SqlClient.SqlConnection)Conexion; com.Transaction = (System.Data.SqlClient.SqlTransaction)MTransaccion; return com; }// end Comando
Como no puedo sobrecargar un mtodo con la misma cantidad de parmetros que recibe, y del mismo tipo, me veo obligado a crear un mtodo extra para crear el comando si queremos ejecutar sentenciasSQL directamente desde la App.
1 protected override System.Data.IDbCommand ComandoSql(string comandoSql) 2 { 3 var com = new System.Data.SqlClient.SqlCommand(comandoSql, (System.Data.SqlClient.Sq 4 (System.Data.SqlClient.SqlTransaction)MTransaccion); return com; 5 }// end Comando
Ya en este punto si intentamos programar en capas, supongo que sabemos para que sirve un DataAdapter, simplemente veremos como crearlo y asociarlo con el commando a su vez, este mtodo es para las llamadas a Procedimientos
1 protected override System.Data.IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, 2 { 3 var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCommand) 4 Comando(procedimientoAlmacenado)); if (args.Length != 0) 5 6 CargarParametros(da.SelectCommand, args); return da; 7 } // end CrearDataAdapter
La siguiente es casi los mismo que la anterior, pero para ejecucin dequerys SQL
1 protected override System.Data.IDataAdapter CrearDataAdapterSql(string comandoSql) 2 { 3 var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCommand) 4 ComandoSql(comandoSql)); return da; 5 } // end CrearDataAdapterSql
Nada ms queda crear los constructores, creamos 4 sobrecargas del mismo segn lo que necesitemos ms adelante.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public SqlServer() { Base = ""; Servidor = ""; Usuario = ""; Password = ""; }// end DatosSQLServer public SqlServer(string cadenaConexion) { CadenaConexion = cadenaConexion; }// end DatosSQLServer public SqlServer(string servidor, string @base) { Base = @base; Servidor = servidor; }// end DatosSQLServer public SqlServer(string servidor, string @base, string usuario, string password) { Base = @base; Servidor = servidor; Usuario = usuario; Password = password; }// end DatosSQLServer
1 using System; 2 3 namespace AccesoDatos 4 { public class SqlServer : GDatos 5 6 { 7 /* 8 * Continuaremos con el mtodo Comando, procediendo de igual forma que en los anter 9 * En este caso, adems, implementaremos un mecanismo de preservacin de los Coma 10 * para acelerar su utilizacin. Esto es, cada procedimiento que sea accedido, se g 11 * en memoria hasta que la instancia del objeto se destruya. Para ello, declararemo
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
* como HashTable para la clase, con el modificador Shared (compartida) que permite * persistir la misma entre creaciones de objetos */ static readonly System.Collections.Hashtable ColComandos = new System.Collections.H public override sealed string CadenaConexion { get { if (MCadenaConexion.Length == 0) { if (MBase.Length != 0 && MServidor.Length != 0) { var sCadena = new System.Text.StringBuilder(""); sCadena.Append("data source=<SERVIDOR>;"); sCadena.Append("initial catalog=<BASE>;"); sCadena.Append("user id=<USER>;"); sCadena.Append("password=<PASSWORD>;"); sCadena.Append("persist security info=True;"); sCadena.Append("user id=sa;packet size=4096"); sCadena.Replace("<SERVIDOR>", Servidor); sCadena.Replace("<BASE>", Base); sCadena.Replace("<USER>", Usuario); sCadena.Replace("<PASSWORD>", Password);
return sCadena.ToString(); } throw new Exception("No se puede establecer la cadena de conexin en la } return MCadenaConexion = CadenaConexion; }// end get set { MCadenaConexion = value; } // end set }// end CadenaConexion
/* * Agregue ahora la definicin del procedimiento CargarParametros, el cual deber a * al parmetro que corresponda (considerando que, en el caso de SQLServer, el par * siempre corresponde al return Value del Procedimiento Almacenado). Por otra pa * como la ejecucin de procedimientos almacenados que devuelven un valor como par * la cantidad de elementos en el vector de argumentos, puede no corresponder con l * Por ello, se decide comparar el indicador con la cantidad de argumentos recibido * protected override void CargarParametros(System.Data.IDbCommand Com, System.Obje */ protected override void CargarParametros(System.Data.IDbCommand com, Object[] args) { for (int i = 1; i < com.Parameters.Count; i++) { var p = (System.Data.SqlClient.SqlParameter)com.Parameters[i]; p.Value = i <= args.Length ? args[i - 1] ?? DBNull.Value : null; } // end for } // end CargarParametros
/* * En el procedimiento Comando, se buscar primero si ya existe el comando en dicha * (convertida en el tipo correcto). Caso contrario, se proceder a la creacin del * y su agregado en el repositorio. Dado que cabe la posibilidad de que ya estemos * es necesario abrir una segunda conexin a la base de datos, para obtener la defi * del procedimiento Almacenado (caso contrario da error, por intentar leer sin ten * objeto Transaction correspondiente). Adems, el comando, obtenido por cualquiera * debe recibir la conexin y la transaccin correspondientes (si no hay Transacci * y ese es el valor que se le pasa al objeto Command) */ protected override System.Data.IDbCommand Comando(string procedimientoAlmacenado)
80 { 81 System.Data.SqlClient.SqlCommand com; if (ColComandos.Contains(procedimientoAlmacenado)) 82 83 com = (System.Data.SqlClient.SqlCommand)ColComandos[procedimientoAlmacenado else 84 85 { 86 var con2 = new System.Data.SqlClient.SqlConnection(CadenaConexion); 87 con2.Open(); 88 com = new System.Data.SqlClient.SqlCommand(procedimientoAlmacenado, con2) { 89 System.Data.SqlClient.SqlCommandBuilder.DeriveParameters(com); 90 con2.Close(); 91 con2.Dispose(); 92 ColComandos.Add(procedimientoAlmacenado, com); 93 }//end else 94 com.Connection = (System.Data.SqlClient.SqlConnection)Conexion; 95 com.Transaction = (System.Data.SqlClient.SqlTransaction)MTransaccion; return com; 96 97 }// end Comando 98 protected override System.Data.IDbCommand ComandoSql(string comandoSql) 99 100 { 101 var com = new System.Data.SqlClient.SqlCommand(comandoSql, (System.Data.SqlClie 102 (System.Data.SqlClient.SqlTransaction)MTransaccion); return com; 103 104 }// end Comando 105 106 107 /* 108 * Luego implementaremos CrearConexion, donde simplemente se devuelve una nueva ins 109 * objeto Conexin de SqlClient, utilizando la cadena de conexin del objeto. 110 */ protected override System.Data.IDbConnection CrearConexion(string cadenaConexion) 111 { return new System.Data.SqlClient.SqlConnection(cadenaConexion); } 112 113 114 115 //Finalmente, es el turno de definir CrearDataAdapter, el cual aprovecha el mtodo protected override System.Data.IDataAdapter CrearDataAdapter(string procedimientoAl 116 117 { 118 var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCom if (args.Length != 0) 119 120 CargarParametros(da.SelectCommand, args); return da; 121 122 } // end CrearDataAdapter 123 124 //Finalmente, es el turno de definir CrearDataAdapter, el cual aprovecha el mtodo protected override System.Data.IDataAdapter CrearDataAdapterSql(string comandoSql) 125 126 { 127 var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCom return da; 128 129 } // end CrearDataAdapterSql 130 131 /* 132 * Definiremos dos constructores especializados, uno que reciba como argumentos los 133 * y de base de datos que son necesarios para acceder a datos, y otro que admita di 134 * Los constructores se definen como procedimientos pblicos de nombre New. 135 */ public SqlServer() 136 137 { Base = ""; 138 139 Servidor = ""; 140 Usuario = ""; 141 Password = ""; 142 }// end DatosSQLServer 143 144 public SqlServer(string cadenaConexion) 145 146 { CadenaConexion = cadenaConexion; }// end DatosSQLServer 147
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 }
public SqlServer(string servidor, string @base) { Base = @base; Servidor = servidor; }// end DatosSQLServer public SqlServer(string servidor, string @base, string usuario, string password) { Base = @base; Servidor = servidor; Usuario = usuario; Password = password; }// end DatosSQLServer }// end class DatosSQLServer
Esta es la tercer entrega, probablemente ser la ms corta pero no la ltima an. 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, en ste caso motores de base de datos. Tambin daremos por terminada la capa de Acceso a Datos, entonces as no mezclamos el cdigo y ser ms fcil seguirlo posteriormente. Tambin pertenecer al namespace AccesoDatos. Lo llamo conexin por que es la clase con las otras capas interactuaran en modo directo. Para ello creamos un objeto esttico de la clase GDatos que instanciar de la clase SqlServer. Creo que ya van captando el rumbo de esto no? si crearamos otra clase por ejemplo Oracle.cs o MySQL.cs, solamente cambiariamos una linea de cdigo, donde el objeto GDatos del tipoGDatos, sea SqlServer, Oracle u otro motor que codifiquemos. Podemos hacerlo con ODBC, OleDB para conexiones genricas. No les parece grandioso que solo deban tocar parte de una lnea de cdigo para portar la App a cualquier otro motor de Base de Datos?
1 namespace AccesoDatos 2 { public class Conexion 3 4 { public static GDatos GDatos; 5 public static bool IniciarSesion(string nombreServidor, string baseDatos, string usu 6 7 password) 8 { 9 GDatos = new SqlServer(nombreServidor, baseDatos, usuario, password); return GDatos.Autenticar(); 10 11 } //fin inicializa sesion 12 public static void FinalizarSesion() 13 14 { 15 GDatos.CerrarConexion(); 16 } //fin FinalizaSesion 17 18 }//end class util }//end namespace
En la siguiente entrega veremos como crear la capa de Negocio y vincularla a estas capas de Acceso a Datos. En la cuarta entrega veremos una capa nueva, la capa de Negocios, como ya dije en los artculos anteriores hemos dado por terminado la capa de Acceso a Datos. Aqu es donde diremos como debe procesarse la informacin. Para este caso no voy a crear una estructura compleja de BBDD ya que el cdigo de C# ya lleva bastante, pero reflejar claramente como se usa sta capa en casos ms complejos. Primeramente crearemos una tabla realmente simple, compuesta por 3 campos llamada Cliente.
1 2 3 4 5 6 7 8 9
CREATE TABLE [dbo].[Cliente]( [IdCliente] [tinyint] NOT NULL, [DescripcionCliente] [varchar](50) NOT NULL, [Siglas] [varchar](15) NULL, CONSTRAINT [PK_Cliente] PRIMARY KEY CLUSTERED ( [IdCliente] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS [PRIMARY] ) ON [PRIMARY]
Preferentemente trabajaremos con procedimientos almacenadoscomo Zeus manda. Para crear, listar y buscar tendremos 3 distintos procedimientos.
1 2 3 4 5 6 7 8 1 2 3 4 5 1 2 3 4 5 CREATE PROCEDURE [dbo].[insCliente] @id as int, @nombre varchar(50), @sigla varchar(15) AS BEGIN insert into Cliente values(@id, @nombre, @sigla); END CREATE PROCEDURE [dbo].[slcCliente] AS BEGIN select * from Cliente; END CREATE PROCEDURE [dbo].[slcCliente] AS BEGIN select * from Cliente; END
Con esto ya tenemos creada la estructura de la base de datos completa, al menos hasta donde vamos a utilizar. Estos SP, se pueden considerar parte de la capa de negocios, no precisamente siempre tiene que estar en la aplicacin, es lo que vena escribiendo en las primeras partes del tutorial. Del lado de la aplicacin utilizaremos el mapeo ORM (doy por sabido que conocen que esto, sino pueden investigarlo aqu), ya que es una de la prcticas ms utilizadas y probadas que ahorran cdigo y ayudan a cumplir con varios conceptos de la OOP. Dentro del mismo proyecto de biblioteca de clases, que estamos teniendo en nuestra solucin (hasta ahora no hemos creado ningn tipo de interfaz
de usuario). Creo conveniente crear una carpeta en la raz del proyecto llamada Orm, justamente para seguir la convencin (trato de seguirlas todas, derrepente se me escapa alguna, sabrn disculparme :S). Al crear esta carpeta, y crear clases dentro de ella tambin se crear un nuevo nivel de espacios de nombres: AccesoDatos.Orm. A esta la llamaremos Cliente.cs, y as una clase por cada tabla que tengamos en nuestra BBDD. En ella crearemos 3 variables, que representarn los atributos de Cliente (mapeando los campos de la tabla). Al mismo tiempo de crear estos, crearemos sus setters ygetters.
1 public short IdCliente { get; set; } 2 public string Descripcion { get; set; } 3 public string Sigla { get; set; }
Ahora es momento de utilizar el potencial del cdigo que hemos venido escribiendo, veanlo en accin en los 3 mtodos que crearemos, uno para dar de Alta, un registro de cliente, en la BBDD, otro para buscar, y otro para listar todos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
{ Conexion.GDatos.Ejecutar("insCliente", cliente.IdCliente, cliente.Descripcion, cliente.Sig } public DataTable Listar() { return Conexion.GDatos.TraerDataTable("slcCliente"); } public Cliente Listar(int idCliente) { var cliente = new Cliente(); DataTable dt = Conexion.GDatos.TraerDataTable("slcClienteById", idCliente); if (dt.Rows.Count > 0) { cliente.IdCliente = Convert.ToInt16(dt.Rows[0][0]); cliente.Descripcion = Convert.ToString(dt.Rows[0][1]); cliente.Sigla = Convert.ToString(dt.Rows[0][2]); return cliente; } return null; }
En esta clase, deben crear todos los mtodos, que correspondan a la clase cliente. Pueden crear todos los que quieran, sin importar que sta clase quede muy larga, simplemente deben encargarse, que sea aqu donde corresponde una accin en s. Como ven estamos aplicando sobrecarga de mtodos en sta clase (no lo confundan conpolimorfismo). La clase completa quedara as:
1 2 3 4 5 6 7 8 9 10 using System; using System.Data; namespace AccesoDatos.Orm { public class Cliente { // lista de atributos con setters y getters public short IdCliente { get; set; } public string Descripcion { get; set; }
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 }
// mtodos public void Crear(Cliente cliente) { Conexion.GDatos.Ejecutar("insCliente", cliente.IdCliente, cliente.Descripcion, cli public DataTable Listar() { return Conexion.GDatos.TraerDataTable("slcCliente"); } public Cliente Listar(int idCliente) { var cliente = new Cliente(); DataTable dt = Conexion.GDatos.TraerDataTable("slcClienteById", idCliente); if (dt.Rows.Count > 0) { cliente.IdCliente = Convert.ToInt16(dt.Rows[0][0]); cliente.Descripcion = Convert.ToString(dt.Rows[0][1]); cliente.Sigla = Convert.ToString(dt.Rows[0][2]); return cliente; } return null; } }
Con sto vemos como se implementa la capa de negocios. En la siguiente entrega veremos ya la capa de Presentacin, en lo posible, utilizares todas las clases hechas ya incluida sta en una aplicacinWinForm y otra WebForm, vern que no slo es portable a cualquier proveedor de datos, sino que es portable a distintas plataformas de ejecucin. Con sta entrega cumpliremos con la capa de Presentacin,utilizaremos todo lo que hemos visto hasta ahora aplicados a una interfaz de usuario, y como lo promet, lo veremos implementado enwinForm como en webForm. El primer ejemplo ser Desktop, crearemos un formulario con una apariencia semejante al que ven en la imagen.
Evidentemente, un sistema real no lo harn as, el botn conectar emula el comportamiento de una pantalla de login, el boton crear mandar a la BBDD los datos de la caja, 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. Otra implementacin interesante sera agregarle un identitya la tabla cliente, pero para el ejemplo ya servir esto. Para esto crearemos 2 proyectos ms dentro de nuestra solucin, uno ser una aplicacin Windows Form con Visual C#, y la otra un sitio Web. El cdigo que pondremos en el botn conectar es como sigue, recuerden que es conveniente armarlo dinamicamente con una pantalla de login especialmente el usuario y el pass. Con esto logramos armar toda la capa de Acceso a Datos, no se mantiene conectada la aplicacin, sino solo sus valores de conexin en memoria.
1 2 3 4 5 6 7 8 9 try { Conexion.IniciarSesion("127.0.0.1", "Queryable", "sa", "123"); MessageBox.Show(String.Format("{0}", "Se conecto exitosamente")); } catch (Exception ex) { MessageBox.Show(ex.Message); }
Para crear un nuevo cliente instanciamos un objeto cliente del tipo Cliente, le seteamos sus atributos, a partir de los valores de la caja de texto, e invocamos el mtodo crear enviandole el nuevo objeto cliente.
1 var cliente = new Cliente(); 2 try
3 4 5 6 7 8 9 10 11 12 13 14
{ cliente.IdCliente = Convert.ToInt16(txtIdCliente.Text); cliente.Descripcion = txtDescripcionCliente.Text; cliente.Sigla = txtSiglaCliente.Text; cliente.Crear(cliente); MessageBox.Show(String.Format("{0}", "Se creo exitosamente")); } catch (Exception ex) { MessageBox.Show(ex.Message); }
Luego de esto escribimos el cdigo de listado de todos los clientes, y lo cargamos en la grilla. Se dan cuenta que necesitamos muy pocas lineas de cdigo en la capa de Presentacin, y que no tiene una dependencia de la BBDD?. Si se dan cuenta, cuando vamos a devolver muchos registros no podemos utilizar un montn de instancias de Cliente, sino simplemente devolvemos un DataTable, DataSet o DataReader.
1 2 3 4 5 6 7 8 9 var cliente = new Cliente(); try { grilla.DataSource = cliente.Listar(); } catch (Exception ex) { MessageBox.Show(ex.Message); }
Finalmente el cdigo de bsqueda quedara algo asi. Cmo sabemos que si buscamos por la PK, siempre nos devolver un slo registro, lo podemos crear como un objeto Cliente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var cliente = new Cliente(); try { cliente = cliente.Listar(Convert.ToInt16(txtIdCliente.Text)); if (cliente != null) { txtDescripcionCliente.Text = cliente.Descripcion; txtSiglaCliente.Text = cliente.Sigla; } else { MessageBox.Show(String.Format("{0}", "No existia el cliente buscado")); } } catch (Exception ex) { MessageBox.Show(ex.Message); }
namespace Test
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 65 66 67 68 69 70 71 72 73 } 74
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnConectar_Click(object sender, EventArgs e) { try { Conexion.IniciarSesion("127.0.0.1", "Queryable", "sa", "***"); MessageBox.Show(String.Format("{0}", "Se conecto exitosamente")); } catch (Exception ex) { MessageBox.Show(ex.Message); } } private void btnListar_Click(object sender, EventArgs e) { var cliente = new Cliente(); try { grilla.DataSource = cliente.Listar(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } private void btnCrear_Click(object sender, EventArgs e) { var cliente = new Cliente(); try { cliente.IdCliente = Convert.ToInt16(txtIdCliente.Text); cliente.Descripcion = txtDescripcionCliente.Text; cliente.Sigla = txtSiglaCliente.Text; cliente.Crear(cliente); MessageBox.Show(String.Format("{0}", "Se creo exitosamente")); } catch (Exception ex) { MessageBox.Show(ex.Message); } } private void btnBuscarById_Click(object sender, EventArgs e) { var cliente = new Cliente(); try { cliente = cliente.Listar(Convert.ToInt16(txtIdCliente.Text)); if (cliente != null) { txtDescripcionCliente.Text = cliente.Descripcion; txtSiglaCliente.Text = cliente.Sigla; } else { MessageBox.Show(String.Format("{0}", "No existia el cliente buscado")); }
75 76 77 78 79 80 }
Con respecto a la parte web, tendremos 2 partes, el cdigo ASP.net, y el c#, veamoslo, 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Cliente.aspx.cs" Inherits="Cliente"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/ transitional.dtd"> <html xmlns="http://www.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" /> <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 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 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
using System; // using AccesoDatos; public partial class Cliente : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnConectar_Click(object sender, EventArgs e) { try { lblEstado.Text = ""; AccesoDatos.Conexion.IniciarSesion("127.0.0.1", "Queryable", "sa", "***"); lblEstado.Text = String.Format("{0}", "Se conecto exitosamente"); } catch (Exception ex) { lblEstado.Text = String.Format(ex.Message); } } protected void btnListar_Click(object sender, EventArgs e) { var cliente = new AccesoDatos.Orm.Cliente(); try { grilla.DataSource = cliente.Listar(); grilla.DataBind(); lblEstado.Text = String.Format("{0}", "Se listo exitosament"); } catch (Exception ex) { lblEstado.Text = ex.Message; } } protected void btnCrear_Click(object sender, EventArgs e) { var cliente = new AccesoDatos.Orm.Cliente(); try { cliente.IdCliente = Convert.ToInt16(txtIdCliente.Text); cliente.Descripcion = txtDescripcionCliente.Text; cliente.Sigla = txtSiglasCliente.Text; cliente.Crear(cliente); lblEstado.Text = String.Format("{0}", "Se creo exitosamente"); } catch (Exception ex) { lblEstado.Text = ex.Message; } } protected void btnListarById_Click(object sender, EventArgs e) { var cliente = new AccesoDatos.Orm.Cliente(); try { cliente = cliente.Listar(Convert.ToInt16(txtIdCliente.Text)); if (cliente != null) { txtDescripcionCliente.Text = cliente.Descripcion; txtSiglasCliente.Text = cliente.Sigla; } else { lblEstado.Text = String.Format("{0}", "No existia el cliente buscado"); }
69 70 71 72 73 74 75 }
Si pueden agregar/aportar mejoras y funcionalidad a estas clases, sern bienvenidas de hecho ste cdigo lo creo un MVP de Microsoft y yo le agregue algunas funcionalidades..