post icon

Como programar en n-Capas con C# (Parte 5)

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 en winForm como en webForm.

El primer ejemplo será Desktop, crearemos un formulario con una apariencia semejante al que ven en la imagen.

frmCliente

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 identity a 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.

1
2
3
4
5
6
7
8
9
try
{
	Conexion.IniciarSesion("127.0.0.1", "Queryable", "sa", "123");
	MessageBox.Show(String.Format("{0}", "Se conecto exitosamente"));
}
catch (Exception ex)
{
	MessageBox.Show(ex.Message);
}

Para crear un nuevo cliente instanciamos un objeto cliente del tipo Cliente, le seteamos sus atributos, a partir de los valores de la caja de texto, e invocamos el método crear enviandole el nuevo objeto cliente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.

1
2
3
4
5
6
7
8
9
var cliente = new Cliente();
try
{
	grilla.DataSource = cliente.Listar();
}
catch (Exception ex)
{
	MessageBox.Show(ex.Message);
}

Finalmente el 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var cliente = new Cliente();
try
{
	cliente = cliente.Listar(Convert.ToInt16(txtIdCliente.Text));
 
	if (cliente != null)
	{
		txtDescripcionCliente.Text = cliente.Descripcion;
		txtSiglaCliente.Text = cliente.Sigla;
	}
	else
	{ MessageBox.Show(String.Format("{0}", "No existia el cliente buscado")); }
}
catch (Exception ex)
{
	MessageBox.Show(ex.Message);
}

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
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);
            }
        }
 
        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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Cliente.aspx.cs" Inherits="Cliente" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/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>
        <br />
        <hr style="margin-top: 0px; margin-bottom: 0px" />
        <asp:GridView ID="grilla" runat="server" CellPadding="4" ForeColor="#333333" 
            GridLines="None">
            <AlternatingRowStyle BackColor="White" />
            <EditRowStyle BackColor="#2461BF" />
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <RowStyle BackColor="#EFF3FB" />
            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <SortedAscendingCellStyle BackColor="#F5F7FB" />
            <SortedAscendingHeaderStyle BackColor="#6D95E1" />
            <SortedDescendingCellStyle BackColor="#E9EBEF" />
            <SortedDescendingHeaderStyle BackColor="#4870BE" />
        </asp:GridView>
        <asp:Label ID="Label3" runat="server" Text="Estado:"></asp:Label>
        <asp:Label ID="lblEstado" runat="server"></asp:Label>
        <br />
        <asp:Button ID="btnConectar" runat="server" Text="Conectar" 
            onclick="btnConectar_Click" />
        <asp:Button ID="btnCrear" runat="server" onclick="btnCrear_Click" 
            Text="Crear" />
        <asp:Button ID="btnListar" runat="server" onclick="btnListar_Click" 
            Text="Listar" />
        <asp:Button ID="btnListarById" runat="server" onclick="btnListarById_Click" 
            Text="Listar By Id" />
        <br />
 
    </div>
    </form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using System;
 
// using AccesoDatos;
 
public partial class Cliente : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 
    }
    protected void btnConectar_Click(object sender, EventArgs e)
    {
        try
        {
            lblEstado.Text = "";
            AccesoDatos.Conexion.IniciarSesion("127.0.0.1", "Queryable", "sa", "***");
            lblEstado.Text = String.Format("{0}", "Se conecto exitosamente");
        }
        catch (Exception ex)
        {
            lblEstado.Text = String.Format(ex.Message);
        }
    }
    protected void btnListar_Click(object sender, EventArgs e)
    {
        var cliente = new AccesoDatos.Orm.Cliente();
        try
        {
            grilla.DataSource = cliente.Listar();
            grilla.DataBind();
            lblEstado.Text = String.Format("{0}", "Se listo exitosamentë");
        }
        catch (Exception ex)
        {
            lblEstado.Text = ex.Message;
        }
    }
    protected void btnCrear_Click(object sender, EventArgs e)
    {
        var cliente = new AccesoDatos.Orm.Cliente();
        try
        {
            cliente.IdCliente = Convert.ToInt16(txtIdCliente.Text);
            cliente.Descripcion = txtDescripcionCliente.Text;
            cliente.Sigla = txtSiglasCliente.Text;
 
            cliente.Crear(cliente);
            lblEstado.Text = String.Format("{0}", "Se creo exitosamente");
        }
        catch (Exception ex)
        {
            lblEstado.Text = ex.Message;
        }
    }
    protected void btnListarById_Click(object sender, EventArgs e)
    {
        var cliente = new AccesoDatos.Orm.Cliente();
        try
        {
            cliente = cliente.Listar(Convert.ToInt16(txtIdCliente.Text));
 
            if (cliente != null)
            {
                txtDescripcionCliente.Text = cliente.Descripcion;
                txtSiglasCliente.Text = cliente.Sigla;
            }
            else
            { lblEstado.Text = String.Format("{0}", "No existia el cliente buscado"); }
        }
        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..
Parte 1 | Parte 2 | Parte 3 | Parte 4

Comentarios desde Facebook:

  1. avatar
    robertogp Google Chrome Windows
    21 Enero 2017 at 06:49 #

    hola,, muy bueno el ejemplo,, de donde puedo descargar el fuente ??? no lo encuentro acá.
    gracias

  2. avatar
    Jose SPAIN Mozilla Firefox Windows
    16 Octubre 2014 at 09:42 #

    Hola amigo. Me parece este ejemplo y trabajo estupendo. Sólo me queda una cosa: como queda finalmente la estructura del proyecto?
    Quiero decir, son tres proyectos, uno solo…etc.

    Muchas gracias.

  3. avatar
    yiliman Mozilla Firefox Windows
    7 Agosto 2014 at 10:43 #

    Felicitaciones por sus aportes soy nuevo en esto y quiero aprender pero con una buena metodologia.

  4. avatar
    alfreod CHILE Mozilla Firefox Windows
    13 Diciembre 2013 at 10:32 #

    Exelente ayuda la modifique para mi systema garcias

  5. avatar
    betinho89 MEXICO Google Chrome Windows
    13 Marzo 2013 at 12:02 #

    Que tal buenos días, aquí mi pequeño aporte, trabajo en una empresa de desarrollo, y se esta trabajando en un Sistema ERP que sea independiente del gestor de base de datos (sqlserver, mysql y oracle hasta el momento) y la plataforma que el cliente solicite (web o escritorio), y tomé esta idea para crear las clases (¬¬ primero fui ignorado por ser nuevo, pero ya cuando vieron la facilidad).

    Primero para leer el tipo de gestor y la conexión, en una clase de constantes declare estas variables:

    public class Cls_Constantes
    {
    public static String Gestor_Base_Datos = ConfigurationManager.ConnectionStrings[“Gestor”].ProviderName.ToString();
    public static String Cadena_Conexion = ConfigurationManager.ConnectionStrings[“Gestor”].ConnectionString;
    }

    Y en la clase de conexión cree un método que inicializa la clase GDatos del tipo de gestor que se este utilizando, GDatos la renombre como HelperGenerico, queda como sigue:

    public static void Iniciar_Helper()
    {
    if (Conexion.HelperGenerico == null)
    {
    switch (Cls_Constantes.Gestor_Base_Datos)
    {
    case “SqlClient”:
    Conexion.Iniciar_Sesion(Cls_Constantes.Cadena_Conexion.ToString(), Cls_Constantes.Gestor_Base_Datos.ToString());
    break;
    case “MySqlClient”:
    Conexion.Iniciar_Sesion(Cls_Constantes.Cadena_Conexion.ToString(), Cls_Constantes.Gestor_Base_Datos.ToString());
    break;
    case “OracleClient”:
    Conexion.Iniciar_Sesion(Cls_Constantes.Cadena_Conexion.ToString(), Cls_Constantes.Gestor_Base_Datos.ToString());
    break;
    }
    }
    }

    La configuración del tipo de gestor y la cadena de conexión la guardo en el App.config (escritorio) o el Web.config (web), según sea el caso, y esto seria lo único que se tendría que cambiar para elegir el gestor, queda de la siguiente manera:

    Espero y les ayude mi granito de arena.

    Bueno, solo quisiera que me resolvieran una duda, aquí están en contra de utilizar los procedimientos almacenados (modifique las funciones de la clase GDatos para recibieran las puras sentencias SQL), entonces me pregunto, ¿Que beneficios tiene utilizar procedimientos almacenados?

    Saludos.

    • avatar
      betinho89 MEXICO Google Chrome Windows
      13 Marzo 2013 at 12:05 #

      Se me olvido colocar la función Iniciar_Sesion, aquí se las dejo:

      public static bool Iniciar_Sesion(string Cadena_Conexion, string Gestor)
      {
      try
      {
      switch (Gestor)
      {
      case “SqlClient”:
      HelperGenerico = new SqlServerHelper(Cadena_Conexion);
      break;
      case “MySqlClient”:
      HelperGenerico = new MysqlHelper(Cadena_Conexion);
      break;
      case “OracleClient”:
      HelperGenerico = new OracleHelper(Cadena_Conexion);
      break;
      }
      return true;
      }
      catch
      {
      return false;
      }
      }

    • avatar
      GeekZero PARAGUAY Google Chrome Windows
      13 Marzo 2013 at 14:08 #

      Tiene varias ventajas, y las 2 más grandes son la seguridad y la eficiencia.

      La seguridad por varios motivos, al utilizar procedimientos prácticamente dejas de ser vulnerables a ataques de SQL Injection. Además que a los usuarios de la base de datos, les restringes el acceso directo a las tablas, solo les das permisos de ejecución a los procedimientos almacenados, y tendrás la seguridad que nadie manipulará a su antojo, saltándose las reglas de negocios, ya sea por error o no.

      La otra gran ventaja es el rendimiento que ofrecen los procedimientos almacenados, esto debido a que las querys SQL para ejecutar pasan por 4 pasos:

      1. Validación de la Sintaxis
      2. Compilación
      3. Preparación del plan de ejecución
      4. Ejecución

      Cuando usamos los procedimientos almacenados, se omiten los 3 primeros pasos, ya que no se valida la sintaxis, porque si esta creado es porque ya paso por ese paso, tampoco se compila nuevamente, eso sucedió cuando creaste el procedimiento, el plan de ejecución ya existe y se mantiene una estadistica de uso por lo que no se prepara ni crea, simplemente se trabaja sobre lo que existe, por ende el único paso que realiza es la ejecución en sí del SQL.

      Espero que te haya ayudado 😀

      • avatar
        betinho89 MEXICO Google Chrome Windows
        13 Marzo 2013 at 14:51 #

        Si, gracias, ya me quedo mas claro, porque efectivamente hacemos esos tres primeros pasos en el sistema que se esta creando, se tuvo que crear una clase de Sintaxis, ya que algunas sentencias SQL varían de acuerdo al gestor, aunque lo tedioso seria estar creando los procedimientos almacenados para cada una de las tablas cuando las base de datos son demasiados extensas, tengo un proyecto en Java mucho mas pequeño, voy a poner en practica los SP allí.

        Ha, aprovecho a dejar algo que no salio en mi primer comentario (creo por cuestiones de seguridad, se me olvido quitar algo :oops:), donde se configura el App.config y el Web.config.

        Nuevamente gracias GeekZero 🙂

  6. avatar
    Marcelo ARGENTINA Mozilla Firefox Windows
    5 Febrero 2013 at 08:54 #

    Hola Macs, acabo de compilar todo el código (lo escribí casi todo) y creo que te está faltando indicarle los campos ValueMember y DisplayMember. En mi aplicación en un formulario completo un listbox de la siguiente manera y funciona bien

    var deftablas = new deftablas;
    lstLista.DataSource = detablas.Listar();
    lstLista.ValueMember=”iddft”;
    lstLista.DisplayMember=”descridft”;

    Saludos.

  7. avatar
    macs CHILE Mozilla Firefox Windows
    6 Diciembre 2012 at 17:14 #

    Me parece muy bueno el artículo y me ha sido de gran utilidad, pero tengo que llenar un combobox con usuarios que los entrega un Datatable, y me aparece la excepción System.NullReferenceException

    User u = new User();
    comboBox1.DataSource = u.Listar();

    //método de la Clase User
    public DataTable Listar()
    { return Conexion.GDatos.TraerDataTable(“slcUser”); }

    El procedimiento almacenado funciona correctamente.

    Se agradece el aporte.

  8. avatar
    PTuxon PERU Mozilla Firefox Windows
    28 Noviembre 2012 at 21:21 #

    lo puedes hacer mas basicoooo

  9. avatar
    Alfonso Arce Silvestre Mozilla Firefox Windows
    25 Octubre 2012 at 17:31 #

    No se puede descargar el archivo, lo prodrias volver a subir o si no es mucha molestia enviarlo a mi correo aarces@hotmail.com muchas gracias

  10. avatar
    Cristian Echeverria CHILE Internet Explorer Windows
    25 Septiembre 2012 at 08:11 #

    Hola, si puedes, me mandas el ejemplo a mi correo cristian.echeverria@linkservice.cl, no lo pude bajar lo borraron, saludos gracias.

  11. avatar
    greg COLOMBIA Internet Explorer Windows
    4 Diciembre 2011 at 01:04 #

    Excelente Aporte GEEKZERO, en ORM haz utilizado Entity framework? linq… donde lo ubicarias en programacion a 3 capas? sabes de algun codigo de ejemplo u otro tutorial asi como el que posteaste!!

    ah! otra pregunta has utilizado en este post otra persona menciono el SUBSONIC 4 has trabajado con esta libreria?

    gracias! de nuevo

  12. avatar
    Bladimir Mozilla Firefox Windows
    1 Noviembre 2011 at 15:50 #

    Excelente artículo. Voy a probarlo. Gracias por compartirlo.

  13. avatar
    rodrigob ARGENTINA Internet Explorer Windows
    15 Septiembre 2011 at 17:50 #

    Voy a dar dos enfoques por mi experiencia en el tema de programacion en capas
    No tengo programado como mi primer enfoque mi solucion en capas pero pienso que seria lo ideal
    de acuerdo a las experiencias que obtuve

    No todo es color de rosa, adema de lidiar con la estructura que se hace de muchas formas
    particular a cada programador tambien tenemos que lidiar con algunos
    asuntos extras(la paginacion para bases de datos que tiene muchos registros, tratar de hacer eficiente
    el programa evitando usar objetos pesados para que la aplicacion pueda correr en pc tan potentes, evitar
    injection sql en controles de interfaz)

    1 enfoque de una solucion en capas:

    Proyectos(#):

    #LibreriaHelpRB:
    proyecto tipo dll de utilidad para los distintos motores de bases de datos
    -HelperSql: armado de consultas y actualizaciones para el motor sql server
    -HelperOracle: armado de consultas y actualizaciones para motor oracle
    -HelperMySQL: armado de consultas y actualizaciones para motor MySql
    -HelperDataRB: clase interface adminsitradora que instancia el helper para el motor seteado
    y seria la clase de uso en la capa de datos, luego aqui en esta dll se usara la clase helper del motor correspondiente
    Esta libraria deberia recibir y retornar datos en forma de datatables genericos y no tipados porque se trata de
    generalizar pero entonces en datos al llamar a esta clase se deberia mapear a los objetos de transporte(orm)
    Esta clase debe manejar paginacion o sea al ejecutar una consulta permitir obtenerla por paginas para evitar la sobrecarga de memoria
    en la interfaz cuando se trata de muchisimos registros
    Tmbien eberia controlarse injeccion en las consultas, ya que los datos de los contenedores de transporte
    no deberian tener codigo ejecutable injeccion y deberia controlarse sobre los datos recibidos por parametros
    o contenedores de trasporte

    #Transporte:
    proyecto tipo dll que actua como conteneder de datos para transportar entre metodos y capas
    Los datos de transporte son los mismo cambie el motor de datos de sql a oracle
    por eso de definir en una dll el trasporte, las entidades y listas de entides
    de la solucion, en esta capa no hay metodos para ejecutar, solo contenedores
    de datos

    #Negocio:
    proyecto tipo dll que declara los metodos, reglas y validaciones de negocio
    Los datos que se obtienen en cada metodo de negocio son los mismos cambie o no el motor
    de datos, entonces decimos que el negocio es unico, no desarrollaria aca instruciones
    sql server por ejemplo, hacer select sql server particulares, porque el motor puede o no ser sql,
    entonces negocio es unico para cualquier motor de datos, por ende hay que generalizar
    Si quiero formar una consulta en un metodo de negocio para retornar datos
    podria crear en LibreriaHelpRB, una clase administradora para cada motor
    en la cual indique por separado los campos del select, el from, el where,
    el having, las subconsultas etc, etc como una forma especifica de la clase adminsitradora
    y esta segun sql, oracle u mysql pueda armar la consulta adecuada
    comentario: algo asi hace subsonic 3, tenes una clase donde indicas todas
    las partes pero despues la clase de subsonic arma la consulta final que se ejecuta en el
    motor segun el motor seteado
    ejemplo:

    yo quiero un listado de comprobantes de ventas que fueron emitidos en cta cte filtrado por cliente
    y que esten vencidos esta es una funcion de negocio, defino mi filtro para este metodo y los datos
    a devolver, en la clase de negocio paso eso a datos, datos tendra que llamar al helper para el motor
    correspondiente, armar el select con lso filtros, obetner los datos y en negocio antes de dslir el metodo
    castear ese datatable generico devuelto al contenderor que devuelve el metodo de negocio

    #Datos:
    proyecto tipo dll que declara que recibe transporte de negocio y debe actualizar
    o recibe consultas y debe devolver datos por medio de transporte
    es unico aunque cambie el motor de datos de la solucion, ya que esta capa llama
    de la dll LibreriaHelpRB a la clase HelperDataRB y esta segun el motor seteado
    debe elegir entre que clases ejecutar(HelperSql,HelperOracle o HelperMySQL) y armar
    la actualizacion o consulta
    .Una clase de datos que consulta deberia obtener los datos por medio de la clase
    administradora HelperDataRB de #LibreriaHelpRB y mapear los datos al objeto de transporte
    para poder retornarlos a negocio
    Una clase de datos que actualiza recibe un contendedor de transporte y llamado a
    la clase administradora HelperDataRB de #LibreriaHelpRB deberia mappear primero
    a datatable generico para pasarselo al metodo de actualizacion correspondiente

    #WInRB:
    proyecto tipo windows de entrada a la solucion, podria ser de consola o web
    es la fachada para el cliente o usuario final

    Usos de las capas en la solucion:

    ejemplo1:
    -para un formulario de alta, baja y modificacion:
    en el formulario que esta en el proyecto #WInRB, hago uso de la capa de transporte
    para pasar los datos a negocio al actualizar un cliente
    La clase de negocio de clientes ubicada en #Negocio recibe los datos de transporte
    y mediante la clase adminsitradora HelperDataRB de #LibreriaHelpRB ejecuta
    la sentencia para el motor estabelcido de datos

    ejemplo2:
    -para un informe de comprobantes detallados:
    en el formulario de filtro se piden los rangos de filtro, estos rangos se pasan como transporte
    a negocio y en negocio tengo un metodo para ese informe donde llamo a la clase adminsitradora HelperDataRB
    de #LibreriaHelpRB ejecuta la sentencia para el motor establecido de datos y retorna en un contenedor
    de transporte con los datos

    2 enfoque de una solucion en capas:(crear una capa de datos para cada motor y setear en tiempo de ejecucion la capa
    que se usara deacuerdo al motor establecido)

    otro enfoque es la capa de interfaz o fachada que querramos(winform, web, movil) unica(puede haber mas de una), la capa de negocio unica
    que valida reglas de negocio y pasa los datos a la capa de datos
    y hacemos una capa de datos fisica por cada motor luego por seteo al ejecutarse el programa
    se trabaja con una u otra capa de datos
    algo asi hace subssonic 3, seria un orm, donde elegis una base de datos y te genera todas las entidades
    una por cada tabla para un motor específico con unas plantillas, hay una plantilla para sql server, una para oracle y asi

    Resumiendo:
    la meta de un sistema es cumplir las funciones de negocio requeridas por la que fue
    requerido y diseñado el mismo sistema, estas funciones pueden cambiar con el tiempo,
    puede cambiar el motor de bases de datos por una cuestion de gustos, o por mejoras o necesidades empresariales
    tambien puede cambiar la necesidad de una fachada nueva para hacer ciertas tareas, pero debemos
    conservar el negocio intacto, salvo que cambien las reglas o necesidades de negocio

    tenga el motor que tenga la capa de interfaz o fachada y la capa de negocio no deberian sentirlo si cambia,
    aun asi, la capa de datos tampoco deberia subrir cambios, solo fabricar el helper necesario
    para el nuevo motor en el proyecto #LibreriaHelpRB
    Es decir una capa de datos unica tambien donde casteo a contenedores para devolver datos hacia el cliente
    y casteo a tipos genericos como datatables para actualizar en el motor de base de datos(mapeo relacional)
    con las clases helper y no atar la capa de datos a un motor especifico o tendre que realizar una capa
    de datos para cada motor

    Si siempre usare el mismo motor de datos podria directamente escribir mi capa de datos unica
    con sentencias especificas del motor elegido, por ejemplo desarrollar mi capa de datos para sql server unicamente
    pero si apuntamos a la variedad por necesidades distintas podemos generalizar, aunque seguro cuesta mas
    igualemnet si fueramso a desarrollar una sola capa de datos son utiles las clases helper
    para evitar repeticion de codigo y nuestra solucion sera mas chica en cantidad de lineas y es importante
    porque programar en capas te lleva a termianr teniendo cualuier cantidad de clases y es mejor organizarse
    desde el principio, tambien puende crear una libreria para funciones y controles base ya sea para windows o form

    • avatar
      greg COLOMBIA Internet Explorer Windows
      4 Diciembre 2011 at 00:41 #

      hola rodrigob

      Me gusto mucho la apreciación que le diste a este blog.tengo algunas preguntas sobre las cuales me gustaria que respondieras ya que veo que tienes una buen bagaje sobre el tema de programacion a 3 capas o n-capas.

      1. la paginación: siempre hay que hacer paginación asi sea un sistema que este en crecimiento. y si es un sistema pequeño?
      2. el orm es mejor seguir utilizando el modelamiento directamente en el codigo o seguir con el entity framework? y utilizar linq? sabes de algun ejemplo o codigo que utilice estas herramientas?
      3. es recomendable usar interfaces en muchos ejemplos sobre programacion por capas no los utilizan y tambien se utiliza componentes como dataset como entidad. que puede diferenciar y que implicaciones en tiempo de programación las horas que me conllevarian.
      4. tienes otra capa llamada transporte, realizar otra capa no implicaria mas tiempo en el desarrollo?

      gracias por tus respuestas.

      soy un novato en .net y llevo las malas constumbres que se desarrollaban en visual basic 6

  14. avatar
    Oscar Mozilla Firefox Windows
    8 Septiembre 2011 at 03:53 #

    No podia quedarme mas claro los felicito por ese gran trabajo gracias!!!!

  15. avatar
    Pancho PERU Mozilla Firefox Windows
    25 Agosto 2011 at 14:19 #

    como haria para sacar un listado en un Datagrid de WPF, ya que no tiene la propiedad DataSource

  16. avatar
    Juan MEXICO Mozilla Firefox Windows
    29 Mayo 2011 at 14:26 #

    ¿Saludos, que tan dificil sería adaptarlo para Access como motor de BD? ¿En que clases habria que cambiarle?

    Esque en si lo que necesito es que cuando distribuya mi aplicación MonoUsuario sea access mi motor, pero cuando sea multiusuario optare por SQL Server.
    ¿Tendría que hacer dos proyectos distintos?

  17. avatar
    Fran SPAIN Mozilla Firefox Windows
    18 Mayo 2011 at 12:40 #

    Bueno… Con este codigo funciona pero lo suyo seria que cojiese el nombre de la tabla sin ponerla tu a mano, es decir , a traves de el metodo TraerDataTable

    
    try
       {
        var a = usu.Listar();
        var b = prov.Listar();
        a.TableName = "usuarios";
        b.TableName = "provincias";
    
        DS.Tables.Add(a);
        DS.Tables.Add(b);
                    
        this.comboBox1.DataSource = DS.Tables["usuarios"];       
        this.comboBox1.DisplayMember = "nombre" ;
        this.comboBox1.ValueMember = "clave" ;
                    
       }
    
    
  18. avatar
    Fran SPAIN Mozilla Firefox Windows
    18 Mayo 2011 at 07:28 #

    Hola. quiero meter dos tables en un dataset, pero al hacer esto me sale el siguente error: “A DataTable named ‘Table’ already belongs to this DataSet.”

    Mi codigo es este:

    
    try
                {
                    
                    DS.Tables.Add(usu.Listar());
                    DS.Tables.Add(prov.Listar());
    
                    this.comboBox1.DataSource = DS.Tables[0];
                    MessageBox.Show(DS.Tables[0].TableName);
                    this.comboBox1.DisplayMember = "nombre";
                    this.comboBox1.ValueMember = "clave";
                }
    
                catch (SystemException o)
                {
                    MessageBox.Show(o.Message);
                }
    
    

    PD: despues de meterlos en el data set quiero relacionarlos pero eso ya es otra historia

    Gracias

    • avatar
      GeekZero PARAGUAY Google Chrome Windows
      18 Mayo 2011 at 08:19 #

      Hola Fran, prueba con esto, sino funciona veremos otro camino..

      1
      2
      
      DS.Tables.Add(usu.Listar().TableName = "Usuario");
      DS.Tables.Add(prov.Listar().TableName = "Proveedor");
      • avatar
        Fran SPAIN Mozilla Firefox Windows
        18 Mayo 2011 at 11:01 #

        probando con lo que has puesto sigue sin funcionar , me aparece el siguente error: “Cannot bind to the new display member.
        Parameter name: newDisplayMember”

        Ahora me mete las 2 tablas en el DS pero al rellenar el combobox me da el fallo

        He buscado en google y me dice que el DisplayMember que le asigno al combobox esta mal, pero si a la propiedad datasource del combo le asigno un datatable todo funciona bien,

        Este es mi codigo:

        
        try
                    {
                        
                        DS.Tables.Add(usu.Listar().TableName = "usuarios");
                        DS.Tables.Add(prov.Listar().TableName = "provincias");
        
                        this.comboBox1.DataSource = DS.Tables[0];
                        
                        this.comboBox1.DisplayMember = "nombre" ;
                        this.comboBox1.ValueMember = "clave" ;
                        
                    }
        
                    catch (SystemException o)
                    {
                        MessageBox.Show(o.Message);
                    }
        
        
        
  19. avatar
    Manuel Navas VENEZUELA Google Chrome Windows
    16 Marzo 2011 at 23:09 #

    El código esta sencillamente genial, yo le agregaría (de hecho voy a hacerlo para un proyecto que tengo) una abastraccion mas, la cual seria que los resultsets de mas de 1 objeto sean listas genericas con objetos de la clase que se este pidiendo, saludos y muchisimas gracias

    • avatar
      GeekZero PARAGUAY Google Chrome Windows
      17 Marzo 2011 at 07:15 #

      Que bueno que te haya servido, sería interesante si compartes con la comunidad tus mejoras 😀

      Saludos..

  20. avatar
    Irene NETHERLANDS Mozilla Firefox Windows
    15 Febrero 2011 at 05:36 #

    Muchas gracias GeekZero!
    Extremadamente didáctio y esclarecedor este artículo.

  21. avatar
    Marco Mozilla Firefox Windows
    13 Febrero 2011 at 18:48 #

    Acabo de terminar de leer tu post, me encuentro es pos de iniciar un proyecto personal de desarrollo en C# y por lo leido es genial, felicitaciones, espero poder aportar mas adelante cuando tenga mas avance en C#.. Gracias.

  22. avatar
    GeekZero PARAGUAY Google Chrome Windows
    10 Febrero 2011 at 08:07 #

    Que codigo es lo que no esta disponible? si es por el enlace de gigasize, acabo de comprobarlo, esta en linea..

    • avatar
      emerson Google Chrome Windows
      16 Diciembre 2012 at 17:16 #

      donde lo descargo?….por favor pasame .. nescesito urgente dominar todo lo que concierne a este tema.. por favor .. con urgencia.. emerson_25_2@hotmail.com

      • avatar
        Orison PERU Google Chrome Windows
        18 Agosto 2014 at 09:39 #

        Hola, tambien ando buscando de donde descargar este ejemplo, lo tendras porsiacaso ? Gracias. Mi correo es orisong@gmail.com

  23. avatar
    Your Name PERU Google Chrome Windows
    9 Febrero 2011 at 23:37 #

    que tal, acabo de revisar tu post, esta genial, quize probar tu código, pero no esta disponible, seria bueno que vuelvas a subirlo, de antemano gracias y hasta pronto,

  24. avatar
    marcelom ARGENTINA Internet Explorer Windows
    14 Diciembre 2010 at 14:36 #

    Hola GeekZero. Antes que nada muchas gracias por este excelente post. Quería hacerte una pregunta. En la parte 1 del post dices que el código puede utilizarse desde VS2005 con el framework 2.0 en adelante, sin embargo yo bajé código ejemplo para Firebird y en alguno de los .cs aparece un using Linq. ¿El código de ese ejemplo puedo utilizarlo en VS2005 o no ? entiendo que LINQ es de VS2008 en adelante.
    Gracias nuevamente. Marcelo

    • avatar
      GeekZero PARAGUAY Mozilla Firefox Windows
      14 Diciembre 2010 at 15:03 #

      Hola Marcelo, pasa que el ejemplo con firebird lo agregue recientemente a pedido de uno de los lectores y ya no cuide ese detalle, pero el codigo que ves en la pagina esta basado en el 2.0, si se me escapo en algún lugar simplemente sacalo del using ya que no utilizo en las capas ninguna de las caracteristicas Linq

      • avatar
        marcelom ARGENTINA Internet Explorer Windows
        14 Diciembre 2010 at 15:09 #

        Gracias por tu pronta respuesta. Voy a probar sin el using y cualquier cosa te aviso. Saludos.

        • avatar
          marcelom ARGENTINA Internet Explorer Windows
          18 Diciembre 2010 at 22:15 #

          Geekzero, en GDatos dice
          // asignar el string sql al command
          var com = Comando(procedimientoAlmacenado);

          La keyword VAR ¿no es de C# 3.0 ? no me compila en vs2005.
          Gracias, Marcelo.

        • avatar
          GeekZero PARAGUAY Google Chrome Windows
          19 Diciembre 2010 at 04:12 #

          Asi mismo es de c# 3.0, originalmente cuando lo que subi estuvo para la version 2, pero tuvimos unos problemas con la db de wordpress y se perdieron algunos datos y tuve que reconstruir con lo que tenia, este tema lo hice casi completo de nuevo y como lo habia actualizado ya para mi vs 2008 quedaron algunos problemas como este, me es dificil hacerme de tiempo para reponer todo como estaba antes, pero prometo que cuando pueda lo iré haciendo..

  25. avatar
    Jose E. MEXICO Mozilla Firefox Windows
    14 Diciembre 2010 at 12:33 #

    Saludos, gracias por poner este ejemplo en la web. Mi pregunta es que porque solo puedo iniciar sesión si tengo al usuario sa con un password. He intentado iniciar sesión con un usurio que yo creo en sql server y no funciona, me marca: “Error de inicio en el usuario sa”. ¿Saben a que se debe?

    Pongo el código tal cual y no sirve, mi usuario tiene todos los permisos, etc.
    Conexion.IniciarSesion(“127.0.0.1”, “MyBD”, “myusuario”, “mypass”);

    • avatar
      GeekZero PARAGUAY Mozilla Firefox Windows
      14 Diciembre 2010 at 15:06 #

      Y funciona tu login del sa desde el SSMS? puedes revisar en la clase SqlServer al momento de construir la cadena de conexion si todo esta bien..

      • avatar
        Jose E. MEXICO Mozilla Firefox Windows
        14 Diciembre 2010 at 15:38 #

        Gracias por tu respuesta, si serve en el Management Studio. Mira este es el codigo que tiene la clase SqlServer para crear la cadena:

        sCadena.Append(“data source=;”);
        sCadena.Append(“initial catalog=;”);
        sCadena.Append(“user id=;”);
        sCadena.Append(“password=;”);
        sCadena.Append(“persist security info=True;”);
        sCadena.Append(“user id=sa;packet size=4096”);
        sCadena.Replace(“”, Servidor);
        sCadena.Replace(“”, Base);
        sCadena.Replace(“”, Usuario);
        sCadena.Replace(“”, Password);

        no se si sea por esta linea:
        sCadena.Append(“user id=sa;packet size=4096”);

        ¿Tendrás idea?

      • avatar
        Jose E. MEXICO Mozilla Firefox Windows
        14 Diciembre 2010 at 15:39 #

        Gracias por tu respuesta, si serve en el Management Studio. Mira este es el codigo que tiene la clase SqlServer para crear la cadena:

        sCadena.Append(“data source=;”);
        sCadena.Append(“initial catalog=;”);
        sCadena.Append(“user id=;”);
        sCadena.Append(“password=;”);
        sCadena.Append(“persist security info=True;”);
        sCadena.Append(“user id=sa;packet size=4096”);
        sCadena.Replace(“”, Servidor);
        sCadena.Replace(“”, Base);
        sCadena.Replace(“”, Usuario);
        sCadena.Replace(“”, Password);

        no se si sea por esta linea:
        sCadena.Append(“user id=sa;packet size=4096”);

        ¿Tendrás idea?

        • avatar
          GeekZero PARAGUAY Mozilla Firefox Windows
          14 Diciembre 2010 at 15:51 #

          El problema es como armas la cadena de conexion, el metodo replace no esta bien utilizado. Fijate como esta en el original y como se utiliza el StringBuilder. Con eso ya solucionarias el problema

Responder