programar en c#
DESCRIPTION
tres capasTRANSCRIPT
Como programar en 3 capas con c# y sql server 2008
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 está plagado de sitios donde preguntan cómo 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 primera 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 cómo 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 máquina,
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 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:
12
using System;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
deDeclaració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 #region "Declaración de Variables"
2345678910
protected string MServidor = "";protected string MBase = "";protected string MUsuario = "";protected string MPassword = "";protected string MCadenaConexion = "";protected IDbConnection MConexion;
#endregion
Lo siguiente por hacer es muy sencillo, crear los setters y getters de
nuestros atributos anteriormente definidos:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
#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; }
#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 haceif (MConexion == null)
MConexion = CrearConexion(CadenaConexion);
// si no esta abierta aun la conexion, lo abreif (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
5354555657
datosreturn 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
objetoIDataReader que es una interfaz de implementación general.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
#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;
} // end TraerDataSetSql
// Obtiene un DataTable a partir de un Procedimiento Almacenado.public DataTable TraerDataTable(string procedimientoAlmacenado){ return TraerDataSet(procedimientoAlmacenado).Tables[0].Copy(); } // end TraerDataTable
//Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parámetros.public DataTable TraerDataTable(string procedimientoAlmacenado, params Object[] args){ return TraerDataSet(procedimientoAlmacenado, args).Tables[0].Copy(); } // end TraerDataTable
//Obtiene un DataTable a partir de un Query SQLpublic 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 parámetros.public IDataReader TraerDataReader(string procedimientoAlmacenado, params object[] args){
var com = Comando(procedimientoAlmacenado);
5051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
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 que tengan// definida variables de tipo output, para funciones escalares mas abajo se declara un metodopublic object TraerValorOutput(string procedimientoAlmacenado){
// asignar el string sql al commandvar com = Comando(procedimientoAlmacenado);// ejecutar el commandcom.ExecuteNonQuery();// declarar variable de retornoObject resp = null;
// recorrer los parametros del SPforeach (IDbDataParameter par in com.Parameters)
// si tiene parametros de tipo IO/Output retornar ese valorif (par.Direction == ParameterDirection.InputOutput || par
ParameterDirection.Output)resp = par.Value;
return resp;} // end TraerValor
// Obtiene un Valor a partir de un Procedimiento Almacenado, y sus parámetros.public object TraerValorOutput(string procedimientoAlmacenado, params Object[] args){
// asignar el string sql al commandvar com = Comando(procedimientoAlmacenado);// cargar los parametros del SPCargarParametros(com, args);// ejecutar el commandcom.ExecuteNonQuery();// declarar variable de retornoObject resp = null;
// recorrer los parametros del SPforeach (IDbDataParameter par in com.Parameters)
// si tiene parametros de tipo IO/Output retornar ese valorif (par.Direction == ParameterDirection.InputOutput || par
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 commandvar com = ComandoSql(comadoSql);// ejecutar el commandcom.ExecuteNonQuery();// declarar variable de retornoObject resp = null;
// recorrer los parametros del Query (uso tipico envio de varias sentencias sql en el mismo command)foreach (IDbDataParameter par in com.Parameters)
// si tiene parametros de tipo IO/Output retornar ese valorif (par.Direction == ParameterDirection.InputOutput || par
ParameterDirection.Output)resp = par.Value;
118119120121122123124125126127128129130131132133134135136137138139140
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 Params de Entradapublic Object TraerValorEscalar(string procedimientoAlmacenado, params object[] args){
var com = Comando(procedimientoAlmacenado);CargarParametros(com, args);return com.ExecuteScalar();
} // end TraerValorEscalar
// Obtiene un Valor de una funcion Escalar a partir de un Query SQLpublic 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 métodos abstractos, para que las clases
derivadas estén obligadas a implementarlas a su manera, en un modo
especifico, ya que los objetos connection, command, dataadapter,
son muy específicos y deben ser implementados por cada una.123456789101112131415161718192021222324252627282930313233
#region "Acciones"
protected abstract IDbConnection CrearConexion(string cadena);protected abstract IDbCommand Comando(string procedimientoAlmacenado);protected abstract IDbCommand ComandoSql(string comandoSql);protected abstract IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, params Objectprotected abstract IDataAdapter CrearDataAdapterSql(string comandoSql);protected abstract void CargarParametros(IDbCommand comando, Object[] args);
// metodo sobrecargado para autenticarse contra el motor de BBDDpublic bool Autenticar(){
if (Conexion.State != ConnectionState.Open)Conexion.Open();
return true;}// end Autenticar
// metodo sobrecargado para autenticarse contra el motor de BBDDpublic bool Autenticar(string vUsuario, string vPassword){
MUsuario = vUsuario;MPassword = vPassword;MConexion = CrearConexion(CadenaConexion);
MConexion.Open();return true;
}// end Autenticar
// cerrar conexionpublic void CerrarConexion(){
if (Conexion.State != ConnectionState.Closed)
343536373839404142434445464748495051525354555657585960616263
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 sqlpublic int EjecutarSql(string comandoSql){ return ComandoSql(comandoSql).ExecuteNonQuery(); } // end Ejecutar
//Ejecuta un Procedimiento Almacenado en la base, utilizando los parámetros.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
ParameterDirection.Output)args.SetValue(par.Value, i - 1);
}// end forreturn resp;
} // end Ejecutar
#endregion
Ahora bien, no podemos olvidarnos de la sección transaccional, no se
utiliza normalmente en todos lados desde la aplicación, pero en
procesos dependientes es necesario, así que si necesitamos usarlo,
podemos crearlo de este modo:
12345678910111213141516171819202122232425262728
#region "Transacciones"
protected IDbTransaction MTransaccion;protected bool EnTransaccion;
//Comienza una Transacción en la base en uso.public void IniciarTransaccion(){
try{
MTransaccion = Conexion.BeginTransaction();EnTransaccion = true;
}// end tryfinally{ EnTransaccion = false; }
}// end IniciarTransaccion
//Confirma la transacción activa.public void TerminarTransaccion(){
try{ MTransaccion.Commit(); }finally{
MTransaccion = null;EnTransaccion = false;
}// end finally
29303132333435363738394041424344
}// end TerminarTransaccion
//Cancela la transacción activa.public void AbortarTransaccion(){
try{ MTransaccion.Rollback(); }finally{
MTransaccion = null;EnTransaccion = false;
}// end finally}// end AbortarTransaccion
#endregion
El código completo lo pueden ver aqui:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
using System;using System.Data;
namespace AccesoDatos{ public abstract class GDatos { #region "Declaración de Variables"
protected string MServidor = ""; protected string MBase = ""; protected string MUsuario = ""; protected string MPassword = ""; protected string MCadenaConexion = ""; protected IDbConnection MConexion;
#endregion
#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
495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
// Cadena de conexión 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();
// 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
#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; } // end TraerDataSetSql
// Obtiene un DataTable a partir de un Procedimiento Almacenado. public DataTable TraerDataTable(string procedimientoAlmacenado) { return TraerDataSet(procedimientoAlmacenado).Tables[0].Copy(); } // end TraerDataTable
//Obtiene un DataSet a partir de un Procedimiento Almacenado y sus parámetros. public DataTable TraerDataTable(string procedimientoAlmacenado, params Object[] args { return TraerDataSet(procedimientoAlmacenado, args).Tables[0].Copy(); } // end TraerDataTable
//Obtiene un DataTable a partir de un Query SQL public DataTable TraerDataTableSql(string comandoSql) { return TraerDataSetSql(comandoSql).Tables[0].Copy(); } // end TraerDataTableSql
117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
// 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 parámetros. public IDataReader TraerDataReader(string procedimientoAlmacenado, params object[] { 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 que tengan // definida variables de tipo output, 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(); // 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 == ParameterDirection resp = par.Value; return resp; } // end TraerValor
// Obtiene un Valor a partir de un Procedimiento Almacenado, y sus parámetros. 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 == ParameterDirection 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
185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
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 el mismo command) foreach (IDbDataParameter par in com.Parameters) // si tiene parametros de tipo IO/Output retornar ese valor if (par.Direction == ParameterDirection.InputOutput || par.Direction == ParameterDirection 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 Params de Entrada public Object TraerValorEscalar(string procedimientoAlmacenado, params object[] args { 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 abstract IDbConnection CrearConexion(string cadena); protected abstract IDbCommand Comando(string procedimientoAlmacenado); protected abstract IDbCommand ComandoSql(string comandoSql); protected abstract IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, params protected abstract IDataAdapter CrearDataAdapterSql(string comandoSql); protected abstract 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
253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
// 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
//Ejecuta un Procedimiento Almacenado en la base, utilizando los parámetros. 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 == ParameterDirection args.SetValue(par.Value, i - 1); }// end for return resp; } // end Ejecutar
#endregion
#region "Transacciones"
protected IDbTransaction MTransaccion; protected bool EnTransaccion;
//Comienza una Transacción en la base en uso. public void IniciarTransaccion() { try { MTransaccion = Conexion.BeginTransaction(); EnTransaccion = true; }// end try finally { EnTransaccion = false; } }// end IniciarTransaccion
//Confirma la transacción activa. public void TerminarTransaccion() { try { MTransaccion.Commit(); } finally { MTransaccion = null; EnTransaccion = false; }// end finally }// end TerminarTransaccion
//Cancela la transacción activa.
321322323324325326327328329330331332333334335
public void AbortarTransaccion() { try { MTransaccion.Rollback(); } finally { MTransaccion = null; EnTransaccion = false; }// end finally }// end AbortarTransaccion
#endregion
}// end class gDatos}// end namespace
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 lógicas.
Continuando con la segunda entrega de la programación en n-Capas,
(la primera lo pueden ver aqui). Hasta el momento solo creamos una
clase abstracta que servirá de padre para las demás 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 algún
momento, lo único que deberíamos hacer es agregar una clase
semejante a ésta con la implementación 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 línea de
código en una clase superior que veremos en la tercer entrega.
Para ésta clase el único uso que haremos será del namspace System.
También se encontrará en el mismo namespace que la clase
anteriorAccesoDatos. La misma será una clase que hereda de la clase
GDatos1 using System;
Lo siguiente que crearemos en una colección 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 método que tendrá esta clase, es un método sellado
quesobreescibirá el de su padre, y creará el ConnectionString y lo
retornorá.1 public override sealed string CadenaConexion
23456789
10111213141516171819202122232425262728
{get{
if (MCadenaConexion.Length == 0){
if (MBase.Length != 0 && MServidor.Length != 0){
var sCadena = new System.Text.StringBuilder("");sCadena.Append("data source=;");sCadena.Append("initial catalog=;");sCadena.Append("user id=;");sCadena.Append("password=;");sCadena.Append("persist security info=True;");sCadena.Replace("", Servidor);sCadena.Replace("", Base);sCadena.Replace("", Usuario);sCadena.Replace("", Password);
return sCadena.ToString();}throw new Exception("No se puede establecer la cadena de conexión en la clase
SQLServer");}return MCadenaConexion;
}// end getset{ MCadenaConexion = value; } // end set
}// end CadenaConexion
Ésta es una de las caracteristicas mas llamativas y útiles, que nos
permitirá cargar una cantidad n de parámetros, a los SP que
invocaremos12345678
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; }}
Luego para crear el Comando a ejecutar crearemos un método que
revisará en el hashTable anteriormente creado, si ya se lo ha hecho o
no. En caso que estemos dentro de una transacción conectado,
necesitamos crear una segunda conexión para la lectura de los
parámetros de Procedimiento almacenado.123456789
10111213141516
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();
17181920
ColComandos.Add(procedimientoAlmacenado, com);}//end elsecom.Connection = (System.Data.SqlClient.SqlConnection)Conexion;com.Transaction = (System.Data.SqlClient.SqlTransaction)MTransaccion;return com;
}// end Comando
Como no puedo sobrecargar un método con la misma cantidad de
parámetros que recibe, y del mismo tipo, me veo obligado a crear un
método extra para crear el comando si queremos ejecutar
sentenciasSQL directamente desde la App.12345
protected override System.Data.IDbCommand ComandoSql(string comandoSql){
var com = new System.Data.SqlClient.SqlCommand(comandoSql, (System.Data.SqlClient.SqlConnection(System.Data.SqlClient.SqlTransaction)MTransaccion);
return com;}// end Comando
Ahora crearemos la conexión a partir de la cadena de conexión
12
protected override System.Data.IDbConnection CrearConexion(string cadenaConexion){ return new System.Data.SqlClient.SqlConnection(cadenaConexion); }
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 método es para las
llamadas a Procedimientos
1234567
protected override System.Data.IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, {
var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCommand)Comando(procedimientoAlmacenado));
if (args.Length != 0)CargarParametros(da.SelectCommand, args);
return da;} // end CrearDataAdapter
La siguiente es casi los mismo que la anterior, pero para ejecución
dequery’s SQL12345
protected override System.Data.IDataAdapter CrearDataAdapterSql(string comandoSql){
var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCommand)ComandoSql(comandoSql));
return da;} // end CrearDataAdapterSql
Nada más queda crear los constructores, creamos 4 sobrecargas del
mismo según lo que necesitemos más adelante.
123456789
10
public SqlServer(){
Base = "";Servidor = "";Usuario = "";Password = "";
}// end DatosSQLServer
public SqlServer(string cadenaConexion){ CadenaConexion = cadenaConexion; }// end DatosSQLServer
1112131415161718192021222324
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
El código completo quedaría así:
123456789
10111213141516171819202122232425262728293031323334353637383940414243444546474849
using System;
namespace AccesoDatos{ public class SqlServer : GDatos { /* * Continuaremos con el método Comando, procediendo de igual forma que en los anteriores. * En este caso, además, implementaremos un mecanismo de “preservación” de los Comandos creados, * para acelerar su utilización. Esto es, cada procedimiento que sea accedido, se guardará * en memoria hasta que la instancia del objeto se destruya. Para ello, declararemos una variable * 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.
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 conexión en la clase DatosSQLServer" } return MCadenaConexion = CadenaConexion;
}// end get set { MCadenaConexion = value; } // end set }// end CadenaConexion
5051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
100101102103104105106107108109110111112113114115116117
/* * Agregue ahora la definición del procedimiento CargarParametros, el cual deberá asignar cada valor * al parámetro que corresponda (considerando que, en el caso de SQLServer©, el parameter 0 * siempre corresponde al “return Value” del Procedimiento Almacenado). Por otra parte, en algunos casos, * como la ejecución de procedimientos almacenados que devuelven un valor como parámetro de salida, * la cantidad de elementos en el vector de argumentos, puede no corresponder con la cantidad de parámetros. * Por ello, se decide comparar el indicador con la cantidad de argumentos recibidos, antes de asignar el valor. * protected override void CargarParametros(System.Data.IDbCommand Com, System.Object[] Args) */ 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 Hashtable para retornarla * (convertida en el tipo correcto). Caso contrario, se procederá a la creación del mismo, * y su agregado en el repositorio. Dado que cabe la posibilidad de que ya estemos dentro de una transacción, * es necesario abrir una segunda conexión a la base de datos, para obtener la definición de los parámetros * del procedimiento Almacenado (caso contrario da error, por intentar leer sin tener asignado el * objeto Transaction correspondiente). Además, el comando, obtenido por cualquiera de los mecanismos * debe recibir la conexión y la transacción correspondientes (si no hay Transacción, la variable es null, * y ese es el valor que se le pasa al objeto Command) */ 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) 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
protected override System.Data.IDbCommand ComandoSql(string comandoSql) { var com = new System.Data.SqlClient.SqlCommand(comandoSql, (System.Data.SqlClient(System.Data.SqlClient.SqlTransaction)MTransaccion); return com; }// end Comando
/* * Luego implementaremos CrearConexion, donde simplemente se devuelve una nueva instancia del * objeto Conexión de SqlClient, utilizando la cadena de conexión del objeto. */ protected override System.Data.IDbConnection CrearConexion(string cadenaConexion) { return new System.Data.SqlClient.SqlConnection(cadenaConexion); }
//Finalmente, es el turno de definir CrearDataAdapter, el cual aprovecha el método Comando para crear el comando necesario. protected override System.Data.IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, {
118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCommand if (args.Length != 0) CargarParametros(da.SelectCommand, args); return da; } // end CrearDataAdapter
//Finalmente, es el turno de definir CrearDataAdapter, el cual aprovecha el método Comando para crear el comando necesario. protected override System.Data.IDataAdapter CrearDataAdapterSql(string comandoSql) { var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCommand return da; } // end CrearDataAdapterSql
/* * Definiremos dos constructores especializados, uno que reciba como argumentos los valores de Nombre del Servidor * y de base de datos que son necesarios para acceder a datos, y otro que admita directamente la cadena de conexión completa. * Los constructores se definen como procedimientos públicos de nombre New. */ 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 }// end class DatosSQLServer}
Esta es la tercer entrega, probablemente será la más corta pero no la
última aún. 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.
También daremos por terminada la capa de Acceso a
Datos, entonces así no mezclamos el código y será más fácil seguirlo
posteriormente. También pertenecerá al namespace AccesoDatos. Lo
llamo conexión por que es la clase con las otras capas interactuaran en
modo directo.
Para ello creamos un objeto estático 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 código, donde el objeto GDatos del
tipoGDatos, sea SqlServer, Oracle u otro motor que codifiquemos.
Podemos hacerlo con ODBC, 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? 123456789
101112131415161718
namespace AccesoDatos{ public class Conexion { public static GDatos GDatos; public static bool IniciarSesion(string nombreServidor, string baseDatos, string usuario, password) { GDatos = new SqlServer(nombreServidor, baseDatos, usuario, password); return GDatos.Autenticar(); } //fin inicializa sesion
public static void FinalizarSesion() { GDatos.CerrarConexion(); } //fin FinalizaSesion
}//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 artículos anteriores hemos dado por terminado la
capa de Acceso a Datos.
Aquí es donde diremos como debe procesarse la información. Para este
caso no voy a crear una estructura compleja de BBDD ya que el código
de C# ya lleva bastante, pero reflejará claramente como se usa ésta
capa en casos más complejos.
Primeramente crearemos una tabla realmente simple, compuesta por 3
campos llamada Cliente.123456789
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.
12345678
CREATE PROCEDURE [dbo].[insCliente]@id as int,@nombre varchar(50),@sigla varchar(15)
ASBEGIN
insert into Cliente values(@id, @nombre, @sigla);END
12345
CREATE PROCEDURE [dbo].[slcCliente]ASBEGIN
select * from Cliente;END
12345
CREATE PROCEDURE [dbo].[slcCliente]ASBEGIN
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 aplicación, es lo que venía escribiendo en las primeras partes del
tutorial.
Del lado de la aplicación utilizaremos el mapeo ORM (doy por sabido
que conocen que esto, sino pueden investigarlo aquí), 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. Dentro del mismo proyecto de
biblioteca de clases, que estamos teniendo en nuestra solución (hasta
ahora no hemos creado ningún tipo de interfaz de usuario). Creo
conveniente crear una carpeta en la raíz del proyecto llamada Orm,
justamente para seguir la convención (trato de seguirlas todas,
derrepente se me escapa alguna, sabrán disculparme :S). Al crear esta
carpeta, y crear clases dentro de ella también 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
representarán los atributos de Cliente (mapeando los campos de la
tabla). Al mismo tiempo de crear estos, crearemos
sus setters ygetters.123
public short IdCliente { get; set; }public string Descripcion { get; set; }public string Sigla { get; set; }
Ahora es momento de utilizar el potencial del código que hemos venido
escribiendo, veanlo en acción en los 3 métodos que crearemos, uno para
dar de Alta, un registro de cliente, en la BBDD, otro para buscar, y otro
para listar todos.
123456789
1011121314151617181920
{ Conexion.GDatos.Ejecutar("insCliente", cliente.IdCliente, cliente.Descripcion, cliente.Sigla}
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 métodos, 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 acción en sí. Como ven estamos aplicando
sobrecarga de métodos en ésta clase (no lo confundan
conpolimorfismo).
La clase completa quedaría así:123456789
10111213141516171819202122232425262728293031323334
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; } public string Sigla { get; set; }
// métodos public void Crear(Cliente cliente) { Conexion.GDatos.Ejecutar("insCliente", cliente.IdCliente, cliente.Descripcion, cliente}
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;
3536
} }}
Con ésto vemos como se implementa la capa de negocios. En la
siguiente entrega veremos ya la capa de Presentación, en lo posible,
utilizares todas las clases hechas ya incluida ésta en una
aplicaciónWinForm y otra WebForm, verán que no sólo es portable a
cualquier proveedor de datos, sino que es portable a distintas
plataformas de ejecución.
Con ésta entrega cumpliremos con la capa de
Presentación,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 harán así, el botón 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 implementación interesante sería agregarle
un identitya la tabla cliente, pero para el ejemplo ya servirá esto. Para
esto crearemos 2 proyectos más dentro de nuestra solución, uno será
una aplicación Windows Form con Visual C#, y la otra un sitio Web.
El código que pondremos en el botón 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 aplicación,
sino solo sus valores de conexión en memoria.
123456789
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 método crear enviandole el nuevo objeto cliente.
123456789
1011121314
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);}
Luego de esto escribimos el código de listado de todos los clientes, y lo
cargamos en la grilla. Se dan cuenta que necesitamos muy pocas
lineas de código en la capa de Presentación, y que no tiene una
dependencia de la BBDD?. Si se dan cuenta, cuando vamos a
devolver muchos registros no podemos utilizar un montón de instancias
de Cliente, sino simplemente devolvemos un DataTable, DataSet o
DataReader.123456789
var cliente = new Cliente();try{
grilla.DataSource = cliente.Listar();}catch (Exception ex){
MessageBox.Show(ex.Message);}
Finalmente el código de búsqueda quedaría algo asi. Cómo sabemos que
si buscamos por la PK, siempre nos devolverá un sólo registro, lo
podemos crear como un objeto Cliente.
123456789
1011121314151617
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);}
El código completo quedaría así:
123456789
10111213141516171819202122232425262728293031323334353637383940
using System;using System.Windows.Forms;using AccesoDatos;using AccesoDatos.Orm;
namespace Test{ 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); } }
41424344454647484950515253545556575859606162636465666768697071727374757677787980
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")); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } }}
Con respecto a la parte web, tendremos 2 partes, el código ASP.net, y el
c#, veamoslo, se los dejo completamente de una:
123456789
10111213141516171819202122
<%@ 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/DTD/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>
2324252627282930313233343536373839404142434445464748495051525354
<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>
123456789
1011121314151617181920212223242526272829303132333435
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;
36373839404142434445464748495051525354555657585960616263646566676869707172737475
} } 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"); } } catch (Exception ex) { lblEstado.Text = ex.Message; } }}
Si pueden agregar/aportar mejoras y funcionalidad a estas clases, serán
bienvenidas de hecho éste código lo creo un MVP de Microsoft y yo le
agregue algunas funcionalidades..