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).
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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
// 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){
// 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
// 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
// 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,
// 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
// 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
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
// 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);
// 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
//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
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
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
/* * 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, {
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,
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; }