post icon

Como programar en n-Capas con C# – SQL Server (Parte 2)

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 anterior AccesoDatos. La misma será una clase que hereda de la clase GDatos

1
using System;

Lo siguiente que crearemos en una colección de hashtable para preservar por así decirlo los comandos ejecutados y accederlos con mayor velocidad si ya fue ejecutado una vez.

1
static readonly System.Collections.Hashtable ColComandos = new System.Collections.Hashtable();

El primer método que tendrá esta clase, es un método sellado que sobreescibirá el de su padre, y creará el ConnectionString y lo retornorá.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public override sealed string CadenaConexion
{
	get
	{
		if (MCadenaConexion.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 get
	set
	{ 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 invocaremos

1
2
3
4
5
6
7
8
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.

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 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 sentencias SQL directamente desde la App.

1
2
3
4
5
protected override System.Data.IDbCommand ComandoSql(string comandoSql)
{
	var com = new System.Data.SqlClient.SqlCommand(comandoSql, (System.Data.SqlClient.SqlConnection)Conexion, (System.Data.SqlClient.SqlTransaction)MTransaccion);
	return com;
}// end Comando

Ahora crearemos la conexión a partir de la cadena de conexión

1
2
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

1
2
3
4
5
6
7
protected override System.Data.IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, params Object[] args)
{
	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 de query’s SQL

1
2
3
4
5
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.

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

El código completo quedaría así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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.Hashtable();
 
 
        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
 
 
        /*	
         * 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) { 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
 
        protected override System.Data.IDbCommand ComandoSql(string comandoSql)
        {
            var com = new System.Data.SqlClient.SqlCommand(comandoSql, (System.Data.SqlClient.SqlConnection)Conexion, (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, params Object[] args)
        {
            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
 
        //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)ComandoSql(comandoSql));
            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
}

Parte 1 |Parte 3 | Parte 4 | Parte 5

Comentarios desde Facebook:

  1. avatar
    Omar CHILE Google Chrome Windows
    20 Mayo 2014 at 09:00 #

    Excelente articulo estimado pero tengo una duda, si copiaba el código resumen al final del articulo y lo pegaba en mi codigo para echarlo a correr, se quedaba pegada en un bucle al momento de autenticar, sin embargo si pego los codigos uno a uno que vas explicando a lo largo del articulo y los pego, la aplicacion coree sin problemas…al paraer los dodigos tienen diferencias entre el resumen y al ver cada uno, no se si a alguien mas le ha pasado

    Saludos y gracias

  2. avatar
    Marcelo ARGENTINA Mozilla Firefox Windows
    6 Febrero 2013 at 16:42 #

    El mejor artículo sobre programación en capas en C# que he visto, útil y muy didáctico. Modifiqué el código de la clase SqlServer para acceder a Postgresql y funcionó correctamente cuando implementas query sql, pero siempre devuelve errores con procedimientos almacenados con parámetros. Es evidente que Sql Server y Postgresql manejan esto de forma diferente, pero ya los solucioné.

    A continuación transcribo el código para trabajar con Postgresql (es necesario bajar el proveedor Npgsql .)

    Saludos

    using System;
    using System.Data;
    using Npgsql;
    using NpgsqlTypes;

    namespace LsiDatos
    {
    public class ServidorPG : GDatos
    {

    static readonly System.Collections.Hashtable ColComandos = new System.Collections.Hashtable();

    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(“Server=;”);
    sCadena.Append(“Port=5432;”);
    sCadena.Append(“User Id=;”);
    sCadena.Append(“Password=;”);
    sCadena.Append(“Database=;”);
    sCadena.Append(“Encoding=UTF8”);
    sCadena.Replace(“”, Servidor);
    sCadena.Replace(“”, Base);
    sCadena.Replace(“”, Usuario);
    sCadena.Replace(“”, Password);
    return sCadena.ToString();
    }
    throw new Exception(“No se pudo definir la cadena de conexión para la clase DatosPostgresql”);
    }
    return MCadenaConexion = CadenaConexion;

    }// end get
    set
    { MCadenaConexion = value; } // end set
    }// end CadenaConexion

    protected override void CargarParametros(System.Data.IDbCommand com, Object[] args)
    {
    for (int i = 1; i <= com.Parameters.Count; i++)
    {
    var p = (Npgsql.NpgsqlParameter)com.Parameters[i -1];
    p.Value = i <= args.Length ? args[i – 1] ?? DBNull.Value : null;

    } // end for
    } // end CargarParametros

    protected override System.Data.IDbCommand Comando(string procedimientoAlmacenado)
    {
    Npgsql.NpgsqlCommand com;
    if (ColComandos.Contains(procedimientoAlmacenado))
    com = (Npgsql.NpgsqlCommand)ColComandos[procedimientoAlmacenado];
    else
    {
    var con2 = new Npgsql.NpgsqlConnection(CadenaConexion);
    con2.Open();
    com = new Npgsql.NpgsqlCommand(procedimientoAlmacenado, con2);
    com.CommandType = CommandType.StoredProcedure;
    Npgsql.NpgsqlCommandBuilder.DeriveParameters(com);
    con2.Close();
    con2.Dispose();
    ColComandos.Add(procedimientoAlmacenado, com);
    }//end else
    com.Connection = (Npgsql.NpgsqlConnection)Conexion;
    com.Transaction = (Npgsql.NpgsqlTransaction)MTransaccion;
    return com;
    }// end Comando

    protected override System.Data.IDbCommand ComandoSql(string comandoSql)
    {
    var com = new Npgsql.NpgsqlCommand(comandoSql,(Npgsql.NpgsqlConnection)Conexion,(Npgsql.NpgsqlTransaction)MTransaccion);
    return com;
    }// end Comando

    protected override System.Data.IDbConnection CrearConexion(string cadenaConexion)
    {
    return new Npgsql.NpgsqlConnection(cadenaConexion);
    }

    protected override System.Data.IDataAdapter CrearDataAdapter(string procedimientoAlmacenado, params Object[] args)
    {
    var da = new Npgsql.NpgsqlDataAdapter((Npgsql.NpgsqlCommand)Comando(procedimientoAlmacenado));
    if (args.Length != 0)
    CargarParametros(da.SelectCommand, args);
    return da;
    } // end CrearDataAdapter

    protected override System.Data.IDataAdapter CrearDataAdapterSql(string comandoSql)
    {
    var da = new Npgsql.NpgsqlDataAdapter((Npgsql.NpgsqlCommand)ComandoSql(comandoSql));
    return da;
    } // end CrearDataAdapterSql

    public ServidorPG()
    {
    Base = "";
    Servidor = "";
    Usuario = "";
    Password = "";
    }

    public ServidorPG(string cadenaConexion)
    { CadenaConexion = cadenaConexion; }// end DatosSQLServer

    public ServidorPG(string servidor, string @base)
    {
    Base = @base;
    Servidor = servidor;
    }// end DatosSQLServer

    public ServidorPG(string servidor, string @base, string usuario, string password)
    {
    Base = @base;
    Servidor = servidor;
    Usuario = usuario;
    Password = password;
    }// end DatosSQLServer

    }
    }

  3. avatar
    FrankM MEXICO Google Chrome Windows
    11 Julio 2012 at 22:14 #

    hola, buenas noches, estoy siguiendo el titorial, solo que el codigo completo de la segunda parte esta incompleto, me gustaria seguir trabajando en el.

    saludos.

  4. avatar
    Cris CHILE Google Chrome Windows
    26 Agosto 2011 at 16:32 #

    Hola! , me a sido de mucha utilidad el tutorial , pero tengo preguntas que sé que son básicas pero no encuentro la solución y es que al hacer parte del tutorial las variables MBase y MServidor no son reconocidas….

    aque se debe esto? , porque en esta clase no estaban declaradas.

  5. avatar
    Fernando PERU Google Chrome Windows
    15 Julio 2011 at 11:37 #

    Hola, muchas gracias por el articulo esta muy bien comentado y sirve de mucha ayuda para los que nos iniciamos en la programación en 3 capas, tengo una duda en la que espero me puedas ayudar, tengo que ingresar datos a varias tablas se que lo puedo controlar con transacciones desde el visual studio o desde el motor de base de datos, el problema es como podría pasarle los parámetros a las tablas ya que están en maestro detalle, en el maestro no tengo problemas pero os datos del detalle están almacenados en un datagridview, de ante mano muchas gracias por tu articulo y tu ayuda

  6. avatar
    Jose SPAIN Mozilla Firefox Windows
    6 Marzo 2011 at 17:28 #

    En primer lugar, enhorabuena por el artículo, ya que está muy bien explicado. Perdona mi ignorancia, pero ¿cual es la utilidad de las clases abstractas, si debemos hacer una clase para cada unos de los proveedores de acceso a datos?.

    Salu2.

    • avatar
      GeekZero PARAGUAY Google Chrome Windows
      7 Marzo 2011 at 08:28 #

      Que tal Jose, como sabes las clases abstractas no pueden instanciarse por lo que no sirven para ser usados directamente. Simplemente sirven para que el programador esté obligado a implementar todos los métodos, es decir, sirve para armar un esqueleto.

      Si lo deseas puedes cambiarlo sin usar clases abstractas y también funcionará.

      • avatar
        Jose SPAIN Mozilla Firefox Windows
        7 Marzo 2011 at 15:04 #

        Eso es lo que yo pensaba, pero para eso ¿no están las interfaces?, la verdad que tengo un pequeño lio con la diferencia entre interfaces y clases abstractas.
        Seguro que tu puedes iluminarme sobre el tema.

        Gracias.

        • avatar
          GeekZero PARAGUAY Google Chrome Windows
          7 Marzo 2011 at 15:14 #

          Es un poco engorroso explicar la diferencia entre uno y otro ya que son muy parecidos los conceptos. Fijate en este PDF que lo explica bastante bien (Solo que esta explicado con Java, pero el concepto se entiende bien y es un poco largo el contenido)

          http://elvex.ugr.es/decsai/java/pdf/AC-interfaces.pdf

          Saludos.

  7. avatar
    Irene NETHERLANDS Mozilla Firefox Windows
    15 Febrero 2011 at 04:55 #

    MUCHAS GRACIAS!
    Ya se que no estoy respetando el protocolo de la web al escribir en mayúsculas, pues es como gritar en la vida real, pero es lo que estoy haciendo en este momento, agradecertelo a gritos, este artículo es uno de los más esclarecedores que he leído sobre el tema.
    De nuevo, MUCHAS GRACIAS!

  8. avatar
    Prograc MEXICO Mozilla Firefox Windows
    26 Enero 2011 at 19:22 #

    Te felicito por tu aportación, como fuente ilustrativa me sirvió muchisimo, de hecho descargue tu codigo y lo modifiqué un poco, de esto ya hace algunos meses, lo único que le añadí son algunas clases para que se pueda conectar a SqlServer, MySql,Oracle, Firebird y PostgreSql.

    Usé algunas interfaces para un blóque de código que hace las veces de Class Factory pero no estoy muy seguro de que sea la mejor manera de modelar esta librería.

    En el código que modifiqué se inicia sesion de alguna de las siguientes formas:

    Conexion.IniciarSesion("cadena de conexion",EDBMS.SqlServer); //ó
    Conexion.IniciarSesion("cadena de conexion",EDBMS.MySql); //ó
    Conexion.IniciarSesion("cadena de conexion",EDBMS.Oracle); //ó
    Conexion.IniciarSesion("cadena de conexion",EDBMS.Firebird); //ó
    Conexion.IniciarSesion("cadena de conexion",EDBMS.PostgreSql);
    

    EDBMS es un enum.

    Me gustaría compartir el código con comunidad de desarrolladores que visitan tu página, ¿me podrias ayudar echandole un ojo para que evalues si lo podrias publicar en tu sitio?… Gracias.

  9. avatar
    Julian ARGENTINA Google Chrome Windows
    24 Noviembre 2010 at 08:18 #

    Voy a revisarlo bien y terminar de leer todas las partes del articulo, y tambien voy probarlo con el debugger .. si despues me queda alguna duda te comento.
    Muchas gracias por tu tiempo GeekZero

    Saludos

    • avatar
      GeekZero PARAGUAY Google Chrome Windows
      11 Diciembre 2010 at 12:50 #

      Mis disculpas, tenias razón Julian. Estoy con poquísimo tiempo y ahora me hice un hueco para revisar esto, en vez de retornar null debe retornar MCadenaConexion, ya esta hecho el cambio.

      Gracias por la corrección.

      Saludos.

  10. avatar
    GeekZero PARAGUAY Google Chrome Windows
    23 Noviembre 2010 at 23:22 #

    Debe ser así, el atributo MCadenaConexion (es abstracta y su clase hija la debe modificar) de la clase GDatos debe estar vacia y se debe recargar si se desea que soporte conexión a cualquier motor, se lo carga con el valor CadenaConexion, fijate con un paso a paso con la ayuda del debugger..

  11. avatar
    julian ARGENTINA Google Chrome Windows
    23 Noviembre 2010 at 08:56 #

    Me parece que el problema que tuvo manuel es a causa de lo que te estoy diciendo.

  12. avatar
    julian ARGENTINA Google Chrome Windows
    22 Noviembre 2010 at 23:22 #

    Por eso, yo lo que dije es que si el string MCadenaConexion no esta vacio el get retorna null.
    Imaginate que yo inicializo el objeto con el constructor SqlServer(cadenaConexion):
    eso lo que hace es setear la variable MCadenaConexion al el valor que yo le pase por parametro ok?
    Luego de inicializar el objeto de esa manera, cuando se quiera ejecutar una linea que haga una consulta a la propiedad CadenaConexion, esa propiedad va a devolver null.
    Porque la propiedad pregunta:
    if (MCadenaConexion.Length == 0)
    {
    blabla
    }
    return null.
    Ese if va a dar false , entonces va a devolver null.

  13. avatar
    GeekZero PARAGUAY Google Chrome Windows
    22 Noviembre 2010 at 21:38 #

    Hola Julian, no se devuelve null si esta null, se devuelve null cuando la cadena no esta vacia, es para poder preguntar por null cn capas superiores nada mas. Siempre la cadena de conexion se crea mediando los constructores sobrecargados la primera vez. Las siguientes veces “recuerda” el login por así decirlo, deberías hacer un paso a paso para comprender su funcionamiento completo..

  14. avatar
    julian ARGENTINA Google Chrome Windows
    22 Noviembre 2010 at 20:14 #

    Buenas, excelente tu post, estoy siguiendolo para implementarlo en mi proyecto.
    Tengo una duda que no me cierra :
    no entiendo por que en el Getter de CadenaConexion si es que no esta nula devolves null .. que pasa si yo utilizo el constructor que recibe la cadena de conexion y despues quiero Obtenerla? en ese caso no me la va a devolver null, cuando en realidad esta seteada la variable.
    Se crea un poco de conflicto por que uno no puede llegar a saber si la cadena de conexion la paso atravez del constructor o la paso atraves de los parametros server , base, etc.
    No seria mejor intercambiar el return null por return MCadenaConexion ? y en el momento del Get fijarse que si esta nula MCadenaConexion devolver la cadena creada con stringbuilder atravez de las variables conexion,etc . y si no esta nula devolver la variable MCadenaConexion?

    Me explico?
    Disculpa mi ignorancia, todavia nunca programe en 3 capas y estoy intentando comprenderlo.

    Saludos

    Julian

  15. avatar
    GeekZero PARAGUAY Google Chrome Windows
    18 Noviembre 2010 at 00:54 #

    Manuel, esta clase una una cadena de conexion completa.. si quieres modificarla hazlo en el lugar donde esta designado, si quieres hacer eso que dices, vas a necesitar modificar mas código por tu cuenta.
    Por qué no te sirve así como está hecho?

  16. avatar
    Manuel MEXICO Mozilla Firefox Windows
    17 Noviembre 2010 at 23:56 #

    Amigo excelente codigo pero necesito conectarme a una base de datos utilizando una cadena de conexion completa (string). SImplemente agregue el siguiente codigo a la clase conexion pero no funcion a =(. Me dice que The connection string properyty hasn’t benn initialized.

    public static bool IniciarSesion(string cadenaconexion)
    {
    ADatos = new SqlServer(cadenaconexion);
    return ADatos.Autenticar();
    }

  17. avatar
    sussy MEXICO Mozilla Firefox Windows
    5 Noviembre 2010 at 18:38 #

    Si, gracias!!! creo que eso fue lo que paso no alcance a ver bien que también se desplazaba jejejje. Estaba, ya tarde revisando este tutorial (casi dormida) Gracias!!!!!!!!!!!

    Ahora lo que no he logrado es que se conecte a la base de datos, ya tengo todo el ejemplo modifique claro, esta parte del ejercicio:

    AccesoDatos.Conexion.IniciarSesion(\SUSY-PC\SQLEXPRESS\, \bdEjemploCapas\, \Susy-PC\Susy\, \ \);
    MessageBox.Show(String.Format(\{0}\, \Se conecto exitosamente\));

    como mis datos repectivamente, pero me da error que el login falló así que he tratado de modificar pero no puedo encontrae la solución

    No tengo password, para entrar a la bd y esta en autentificación por medio de Windows.

    • avatar
      sussy MEXICO Mozilla Firefox Windows
      5 Noviembre 2010 at 18:40 #

      nose porque se pusieorn las barras esas pero asi no va oks
      solo van dos despues de SUSY……SQLEXPRESS y la otra
      Susy-PC…Susy

      los puntos es donde deven ir dos barras invertidas.

      • avatar
        sussy MEXICO Mozilla Firefox Windows
        5 Noviembre 2010 at 18:43 #

        Esta es mi cadena de conexión (ya mire lo de Code jejejjejee)

        AccesoDatos.Conexion.IniciarSesion(“SUSY-PC\\SQLEXPRESS”, “bdEjemploCapas”, “SUSY-PC\\SUSY”, ” “);

        PS: soy nueva por aquí

    • avatar
      GeekZero PARAGUAY Google Chrome Windows
      5 Noviembre 2010 at 20:55 #

      Si note que faltaban las comillas pero es un detalle nomas.

      Con respecto a eso que quieres hacer, éstas clases no soportan las autenticaciones de Windows, debes activar la autenticación SQL en tu motor o cambiar la clase de conexión.

  18. avatar
    sussy MEXICO Mozilla Firefox Windows
    5 Noviembre 2010 at 03:06 #

    Gracias por el ejemplo realmente lo necesitaba. Solo me queda una duda no logro ver vien como va el codigo.

    public class SqlServer : GDatos \\Tenemos esta clase SqlServer que hereda de la GDatos.
    y la clase GDatos

    pero en la parte de SqlServer no se si podrias poner como queda toda.

    Gracias!!!

    • avatar
      GeekZero PARAGUAY Google Chrome Windows
      5 Noviembre 2010 at 06:48 #

      Hola Sussy, no estoy 100% seguro de haber comprendido tu pregunta, pero me arriesgaré a responder lo que supongo pudo ser.
      En el último bloque código puedes ver todo el código completo de la clase, y si te refieres que no puedes ver como queda porque no entra en la caja, en la parte inferior de la misma verás una barra de desplazamiento para verlo. También puedes ir seleccionando el código copiarlo y pegarlo en otro lugar.
      Es correcta mi respuesta? si no lo es hazmelo saber reformulandomela.
      Saludos.

Trackbacks/Pingbacks

  1. Programación en 3 Capas con C# - Parte 5 | DevTroce.com UNITED STATES WordPress - 8 Noviembre 2012

    […] hecho éste código lo creo un MVP de Microsoft y yo le agregue algunas funcionalidades.. Parte 1 | Parte 2 | Parte 3 | Parte 4 14 julio […]

  2. Como programar en n-Capas con C# (Parte 1) | DevTroce.com WordPress - 15 Agosto 2010

    […] separaré en partes e iré agrengando de a poco las otras clases y luego las otras capas lógicas. Parte 2 | Parte 3 | Parte 4 | Parte […]

  3. Como programar en Capas - Foro SPAIN Netscape Navigator - 25 Julio 2010

    […] […]

  4. Programación en 3 Capas con C# - Parte 4 | DevTroce.com WordPress - 13 Julio 2010

    […] entregas anteriores las pueden encontrar aquí: 1, 2 y 3. Con ésto vemos como se implementa la capa de negocios. En la siguiente entrega veremos ya la […]

  5. Tweets that mention Programacion en 3 Capas con C# - SQL Server | DevTroce.com -- Topsy.com UNITED STATES - 12 Julio 2010

    […] This post was mentioned on Twitter by DevTroce and David Aponte. David Aponte said: RT @cleonati Como programar en n capas en C# Parte 2 http://bit.ly/cLtn45 (continuara) […]

Responder