Software y Aplicaciones Web

Blog de desarrollo de software y aplicaciones web

Comentarios Recientes

Comment RSS

MSDN Home Page (Argentina)


C# Corner


AspAlliance.com

Declaración

Las opiniones en este blog se proporcionan "TAL CUAL", sin garantías,  no confieren derechos y no reflejan, necesariamente, la opinión de quienes me contratan.
Algunas cuestiones que se comentan en el blog no son reales, cualquier similitud con alguna persona viva o muerta no es más que una coincidencia, tampoco significa que necesite terapia, soy asi.

© Copyright 2007-2010

Propaganda

Este sitio implementa publicidad basada en intereses
Oct
3.
2009

  Un poco de ADO.NET - parte 5

En la publicación anterior comenté algo sobre "eficiencia" y es porque estoy convencido que los desarrolladores siempre debemos tener esa palabra en mente; ahora vamos a realizar un ejercicio en el que implementaremos un DataSet con varias tablas, relaciones y además utilizaremos el Cache del servidor ASP para mantener los datos.

En la parte 4 de esta serie de publicaciones hicimos una implementación "económica" para acceder a la información de Provincias, Departamentos y Localidades permitiendo que el usuario seleccione los distintos ítems hasta obtener una Localidad en particular. Digo económica porque utilizamos objetos del tipo SqlDataReader que son más chicos y más rápidos que los DataSet, sin embargo cada vez que un usuario accede a todo el proceso de selección de una localidad se producen innumerables accesos al motor de la base de datos.

Ocurre que la información de Provincias, Departamentos y Localidades rara vez cambia (casi nunca) de manera que esta información es una excelente candidata para ser "cacheada" en RAM evitando de ese modo miles de accesos al servidor de datos. Por supuesto habrá que pensar algún mecanismo para actualizar el cache, lo que dejo para el final de esta publicación.

Por otro lado, lo que vamos a realizar es un objeto complejo que tendrá toda la información necesaria para brindar a cualquier usuario los datos de acuerdo a la selección que va realizando. Esto quiere decir que este objeto complejo tendrá toda la información de Provincias, Departamentos y Localidades con las relaciones necesarias para realizar el filtrado de datos correspondiente.

 

El ejercicio es en realidad un demo, que puede servir como base para que Uds. desarrollen componentes específicos de una aplicación.

La idea es mostrar en pantalla la lista de provincias, cuando el usuario selecciona una de ellas deben aparecer (al lado) los departamentos de esa provincia y cuando el usuario seleccione uno de los departamentos deberán aparecer las localidades de dicho departamento. A continuación está una imagen del diseño que puede funcionar para el ejercicio:

Obviamente lo que estamos por hacer no se puede hacer con los asistentes de Visual Studio (hay que ensuciarse las manos y de una buena vez ponerse a escribir código para eso somos desarrolladores), de modo que el código de la interfaz de usuario puede quedar como sigue:

   1:  <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm7.aspx.cs" Inherits="ADO2.WebForm7" %>
   2:   
   3:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   4:   
   5:  <html xmlns="http://www.w3.org/1999/xhtml" >
   6:  <head runat="server">
   7:      <title></title>
   8:  </head>
   9:  <body>
  10:      <form id="form1" runat="server">
  11:      <div style="font-size:small;">
  12:        <div>
  13:          <asp:Label ID="lblDataOrigin" runat="server" Text=""></asp:Label>
  14:        </div>
  15:        <div>
  16:          <div style="float:left; padding-right:15px; " >
  17:            <asp:GridView ID="gvProvincia" runat="server" AllowPaging="True" PageSize="5" 
  18:              onpageindexchanging="gvProvincia_PageIndexChanging" 
  19:              onselectedindexchanged="gvProvincia_SelectedIndexChanged" SelectedRowStyle-ForeColor="White" SelectedRowStyle-BackColor="#660066">
  20:              <Columns>
  21:                <asp:CommandField ShowSelectButton="True" />
  22:              </Columns>
  23:            </asp:GridView>
  24:          </div>
  25:          <div>
  26:            <div>
  27:              <div style="float:left; padding-right:15px; ">
  28:                <asp:GridView ID="gvDepartamento" runat="server" 
  29:                  onselectedindexchanged="gvDepartamento_SelectedIndexChanged" >
  30:                  <Columns>
  31:                    <asp:CommandField ShowSelectButton="True" />
  32:                  </Columns>
  33:                </asp:GridView>
  34:              </div>
  35:              <div>
  36:                <asp:GridView ID="gvLocalidad" runat="server" >
  37:                </asp:GridView>
  38:              </div>
  39:            </div>
  40:          </div>
  41:        </div>
  42:      </div>
  43:      </form>
  44:  </body>
  45:  </html>

Aprovecho la oportunidad para mostrarles como se realiza una página o formulario web sin utilizar tablas Wink, en este caso los componentes de la interfaz de usuario flotan unos al lado de los otros.

Observen que los componentes solamente incorporan lo necesario para capturar el evento cuando cambia el índice de la selección, y además voy a mostrar cómo se puede hacer para paginar un GridView desde código (esto va sin cargo en esta publicación).

 

Bien como dije, vamos a crear un objeto complejo que tendrá todo lo necesario para mostrar la información de estas tres tablas. De manera que vamos a codificar un método que crea este objeto; no vamos a definir una clase porque el objeto es un DataSet que facilita todo el comportamiento que necesitamos.

   1:      /// <summary>
   2:      /// Carga un DataSet con la información desde la base de datos
   3:      /// Establece las relaciones existentes entre las tablas cargadas
   4:      /// </summary>
   5:      /// <returns>Objeto DataSet</returns>
   6:      protected DataSet MyDataLoad()
   7:      {
   8:        SqlConnection mySqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["Postal"].ConnectionString);
   9:        SqlCommand mySqlCommand = new SqlCommand();
  10:        mySqlCommand.Connection = mySqlConnection;
  11:        DataSet myDataSet = new DataSet();
  12:        SqlDataAdapter mySqlDataAdapter;
  13:   
  14:        mySqlCommand.CommandText = "SELECT [ID], [Nombre] FROM [Provincia]";
  15:        mySqlCommand.CommandType = CommandType.Text;
  16:        mySqlDataAdapter = new SqlDataAdapter(mySqlCommand);
  17:        mySqlDataAdapter.Fill(myDataSet, "Provincia");
  18:   
  19:        mySqlCommand.CommandText = "SELECT [ID], [idProvincia], [Nombre] FROM [Departamento]";
  20:        mySqlCommand.CommandType = CommandType.Text;
  21:        mySqlDataAdapter = new SqlDataAdapter(mySqlCommand);
  22:        mySqlDataAdapter.Fill(myDataSet, "Departamento");
  23:   
  24:        DataColumn myParentColumn = myDataSet.Tables["Provincia"].Columns["ID"];
  25:        DataColumn myChildColumn = myDataSet.Tables["Departamento"].Columns["idProvincia"];
  26:        DataRelation myRealtion = new DataRelation("Provincia_Departamento", myParentColumn, myChildColumn);
  27:        myDataSet.Relations.Add(myRealtion);
  28:   
  29:        mySqlCommand.CommandText = "SELECT [ID], [idDepartamento], [Nombre] FROM [Localidad]";
  30:        mySqlCommand.CommandType = CommandType.Text;
  31:        mySqlDataAdapter = new SqlDataAdapter(mySqlCommand);
  32:        mySqlDataAdapter.Fill(myDataSet, "Localidad");
  33:   
  34:        myParentColumn = myDataSet.Tables["Departamento"].Columns["ID"];
  35:        myChildColumn = myDataSet.Tables["Localidad"].Columns["idDepartamento"];
  36:        myRealtion = new DataRelation("Departamento_Localidad", myParentColumn, myChildColumn);
  37:        myDataSet.Relations.Add(myRealtion);
  38:   
  39:        return myDataSet;
  40:      }

Comentemos lo que hacemos en este método. En las líneas 8 a 17 se declaran y crean los objetos necesarios para obtener desde la base de datos todas las Provincias y se cargan en el DataSet, observen que para ello se "bautiza" la tabla con un nombre (línea 17).

En las líneas 19 a 22 se hace lo mismo con la información de los Departamentos y se cargan en el DataSet también "bautizando" la tabla (interna) que los contiene.

En las líneas 24 a 27 se crea la relación que existen entre cada Provincia y sus Departamentos, esta relación se agrega (línea 27) a la colección de relaciones que mantiene el DataSet.

En las líneas 34 a 37 se hace lo mismo para las Localidades y su relación con los Departamentos.

Finalmente este método devuelve el objeto DataSet con toda esta información.

 

Ahora tenemos que desarrollar un mecanismo que nos permita enlazar los datos con la interfaz de usuario, eso es lo que normalmente se conoce como DataBind. Recordemos que esto es "muy eficiente" de manera que es en este punto donde vamos a incorporar el manejo del Cache que nos brinda el Servidor ASP.

El Cache es un objeto (pedazo de memoria) que está disponible en el servidor ASP para nuestras aplicaciones (un cache por cada aplicación), cuando se debe utilizar y cuando no hacerlo es una decisión arquitectónica y también de desarrollo, en este caso el ejemplo viene como anillo al dedo para utilizarlo.

Voy a describir el método que enlaza los datos por partes para no mezclar la gestión del cache con el proceso de enlazar los datos propiamente dicho:

   1:      /// <summary>
   2:      /// Enlaza los datos con los controles de la interfaz de usuario
   3:      /// La primera vez lo hace desde el motor de base de datos y 
   4:      /// almacena el dataset en el cache del servidor, 
   5:      /// las siguientes veces toma el dataset del cache.
   6:      /// </summary>
   7:      protected void MyDataBind()
   8:      {
   9:        DataSet myDataSet;
  10:   
  11:        if (Cache["MyDataSet"] == null)
  12:        {
  13:          myDataSet = MyDataLoad();
  14:          Cache["MyDataSet"] = myDataSet;
  15:          lblDataOrigin.Text = "Datos desde el <b>Motor de Base de Datos</b>";
  16:        }
  17:        else
  18:        {
  19:          myDataSet = (DataSet)Cache["MyDataSet"];
  20:          lblDataOrigin.Text = "Datos desde el <b>Cache</b>";
  21:        }
  22:   

Este es el código que realiza la tarea, en primer lugar se verifica si el Cache está vació (esto es la primera vez) entonces se obtiene el DataSet del método explicado antes, se guarda el DataSet en el cache y para que veamos como ocurre se indica en un label (que no debe estar en la aplicación final); caso contrario, cuando encontramos algo en el cache y como eso que encontramos lo pusimos nosotros ya sabemos de que se trata de manera que lo podemos tomar en nuestra variable del tipo DataSet y avisamos en el label que lo recuperamos desde el Cache y no del motor de base de datos.

El cache es un objeto que implementa el comportamiento de un Dictionary de modo que se puede acceder mediante un clave (en este caso "MyDataSet").

 

Ahora seguimos con el código que enlaza los datos con la interfaz de usuario:

  23:        gvProvincia.DataSource = myDataSet.Tables["Provincia"];
  24:        gvProvincia.DataKeyNames = new string[] { "ID" };
  25:        gvProvincia.DataBind();
  26:   
  27:        if (gvProvincia.SelectedIndex != -1)
  28:        {
  29:          DataView myDataView = new DataView(myDataSet.Tables["Provincia"]);
  30:          myDataView.Sort = "ID";
  31:          object key = gvProvincia.SelectedValue;
  32:          int index = myDataView.Find(key);
  33:          DataRowView myDataRowView = myDataView[index];
  34:   
  35:          gvDepartamento.DataSource = myDataRowView.CreateChildView("Provincia_Departamento");
  36:          gvDepartamento.DataKeyNames = new string[] { "ID" };
  37:          gvDepartamento.DataBind();
  38:   

Lo que se hace es indicar que el origen de datos para el GridView de Provincias es la tabla "Provincia" que se encuentra dentro del DataSet, se indica que la clave de cada renglón será el campo "ID" (para eso hay que crear un arreglo de string, porque la clave podría estar compuesta por varios campos), se enlaza los datos con el GridView.

A continuación en la línea 27 se averigua si el usuario ha seleccionado algún renglon correspondiente a Provincias y en caso de ser así se procede a enlazar los datos correspondientes a los Departamentos de esa Provincia. Para ello se utiliza una "vista" de la tabla de provincias, observen que el valor seleccionado (SelectedValue) del GridView de Provincias se utiliza para buscar dentro del DataView que nos devuelve el índice que corresponde a la fila donde se encuentra esa información.

Finalmente se indica que el origen de datos del GridView de Departamentos es una vista que se obtiene a partir del renglón (fila) donde se encuentra la provincia por medio de un método que utiliza la relación que existe dentro del DataSet para traer solamente los departamentos correspondientes a esa provincia. Nuevamente se indica que la clave de ordenación (necesaria para poder buscar) es el campo "ID" en este caso el de los Departamentos (hace falta para hacer lo mismo con las Localidades) y se "enlaza" los datos con la interfaz de usuario.

A continuación está el código semejante para las Localidades:

  39:          if (gvDepartamento.SelectedIndex != -1)
  40:          {
  41:            myDataView = new DataView(myDataSet.Tables["Departamento"]);
  42:            myDataView.Sort = "ID";
  43:            myDataRowView = myDataView[myDataView.Find(gvDepartamento.SelectedValue)];
  44:   
  45:            gvLocalidad.DataSource = myDataRowView.CreateChildView("Departamento_Localidad");
  46:            gvLocalidad.DataBind();
  47:          }
  48:          else
  49:          { // Esto hace falta para que desaparezca el GridView
  50:            gvLocalidad.DataSource = null;
  51:            gvLocalidad.DataBind();
  52:          }
  53:        }
  54:        else
  55:        {
  56:          gvLocalidad.DataSource = null;
  57:          gvLocalidad.DataBind();
  58:          gvDepartamento.DataSource = null;
  59:          gvDepartamento.DataBind();
  60:        }
  61:      }

Observen que en este caso se utilizan directamente los valores devueltos por la propiedad SelectedValue y el método Find.

En caso que no haya una selección válida nos aseguramos que los GridView tanto de Localidades y Departamentos desaparezcan, porque no deben mostrarse si no se ha seleccionado nada.

 

El código que atrapa los eventos de cambio de índice seleccionado es el siguiente:

   1:      /// <summary>
   2:      /// Captura el cambio de selección en el GridView de Provincias
   3:      /// </summary>
   4:      /// <param name="sender">Objeto que ejecuta el evento</param>
   5:      /// <param name="e">Información del evento</param>
   6:      protected void gvProvincia_SelectedIndexChanged(object sender, EventArgs e)
   7:      {
   8:        gvDepartamento.SelectedIndex = -1;
   9:        MyDataBind();
  10:      }
  11:      /// <summary>
  12:      /// Captura el cambio de selección en el GridView de Departamentos
  13:      /// </summary>
  14:      /// <param name="sender">Objeto que ejecuta el evento</param>
  15:      /// <param name="e">Información del evento</param>
  16:      protected void gvDepartamento_SelectedIndexChanged(object sender, EventArgs e)
  17:      {
  18:        MyDataBind();
  19:      }

Observen que al cambiar la selección de Provincia nos aseguramos que se anule la actual selección de Departamentos lo que provocará la "desaparición" del GridView correspondiente a las Localidades.

 

Finalmente el código que permite paginar un GridView:

   1:      /// <summary>
   2:      /// Captura el intento de cambio de página en el GridView de Provincias
   3:      /// </summary>
   4:      /// <param name="sender">Objeto que ejecuta el evento</param>
   5:      /// <param name="e">Información del evento</param>
   6:      protected void gvProvincia_PageIndexChanging(object sender, System.Web.UI.WebControls.GridViewPageEventArgs e)
   7:      {
   8:        // Si cambiamos de página en las provincias
   9:        // dejan de ser validos los otros GridView
  10:        gvProvincia.SelectedIndex = -1;
  11:        gvDepartamento.SelectedIndex = -1;
  12:        gvProvincia.PageIndex = e.NewPageIndex;
  13:        MyDataBind();
  14:      }

Observen que al cambiar la página de Provincias se debe cancelar la selección actual. Es importante destacar que para hacer esto se debe capturar el evento "antes que se cambie la página" este evento es el "Changing" no el "Changed".

 

Como dije al principio, esta cuestión de poner "cosas" en el cache del servidor ASP necesita de un mecanismo que "refresque" el cache cada tanto tiempo. Bien para eso les muestro cómo funciona el siguiente método:

   1:        if (Cache["MyDataSet"] == null)
   2:        {
   3:          myDataSet = MyDataLoad();
   4:          //Cache["MyDataSet"] = myDataSet;
   5:          Cache.Insert("MyDataSet", myDataSet, null, DateTime.UtcNow.AddMinutes(1), System.Web.Caching.Cache.NoSlidingExpiration);
   6:          lblDataOrigin.Text = "Datos desde el <b>Motor de Base de Datos</b>";
   7:        }

Se reemplaza la línea 4 por la línea 5, en donde estamos utilizando el método Insert que publica el objeto cache. Este método nos permite insertar un objeto en el cache indicando el nombre o clave con el que accederemos, el objeto mismo, un valor null (que por ahora no voy a explicar porque complicaría todo), un valor de tiempo (en este caso estoy tomando la fecha y hora UTC actual más 1 minuto) que sirve para decirle al objeto cache que cuando se cumpla ese tiempo SUPRIMA EL OBJETO DEL CACHE !!!, y el último parámetro que indica que no se debe reiniciar la cuenta cada vez que se accede al objeto en el cache.

Lo que se logra con esto es que cada 1 minuto se volverá a tomar los datos desde la base de datos, de manera que si alguien "agregó" o "cambió" los datos en la misma, esos cambios aparecerán en el cache. Por supuesto se debe decidir cada cuanto tiempo se "refresca" el cache.

 

Espero que les sirva, espero comentarios.

 

Nota: No se olviden de invocar a MyDataBind() desde el Page_Load (solamente cuando se carga la página).

Para leer fuera de línea: Un Poco de ADO NET - parte 5.pdf (107,01 kb)







Comments (11) -

Luis Argentina

Tuesday, October 06, 2009 9:53 AM

Luis

muy bueno el resumen sobre lo que estuvimos viendo sobre ADO .NET

aldo Argentina

Friday, October 09, 2009 9:22 AM

aldo

MUY  BUENO ESPERAMOS MAS JE, GRACIAS

pedro Argentina

Sunday, October 18, 2009 2:40 PM

pedro

profe. como siempre tengo errores y aun no se por q::::

este es el unico y primerode ellos:

DataRowView myDataRowView = myDataView(index)

ERROR: myDatView es variable pero se utiliza como método
SOLUCIONADO: suprimi los parentesis por el corchete, entonces mi pregunta es: ¿que significa una variable con corchetes???

gracias Prof, hasta luego

jtentor

Sunday, October 18, 2009 8:27 PM

jtentor

Pedro, MyDataView es un objeto del tipo DataView que en su estructura interna tiene una colección (probablemente un ArrayList) de objetos del tipo DataViewRow, en la implementación del DataView se puede acceder a cada una de esas filas (row) indicando el subindice (en base cero) de la fila, por eso se deben utilizar los corchetes. Supongamos un objeto pepe al que se le implementó el acceso indizado entonces se puede acceder a los elementos que están dentro de pepe mediante pepe[0], pepe[5] o pepe[index], en cambio si se pretende usar pepe(index) eso es el llamado a un método por eso el mensaje de error es que es una variable y se utiliza como método.
En una teoría de listas muestro cómo se puede acceder a una lista doblemente enlazada mediante arreglos, si tenés tiempo fijate en esta publicación (www.fi.unju.edu.ar/.../...ncadenadas_Avanzadas.pdf página 13.

Alex Spain

Friday, January 08, 2010 7:48 AM

Alex

Hola nuevamente. Tengo ciertas dudas referentes a ADO.NET, aunque no son referidas al contenido de este buen artículo, con tu permiso las escribo aquí. Las dudas son las siguientes:

1. Sin hacer uso de transacciones, considerando las operaciones sobre la base de datos a nivel de instrucción; con ADO.NET, ¿como se implementa el bloqueo pesimista?.

2. Si por ejemplo empleo un DataAdapter (o TableAdapter) para realizar operaciones sobre la base de datos, ¿qué tipo de bloqueo se aplica por defecto?, ¿como lo puedo modificar sin emplear transacciones?.

3. Si por el contrario realizo las operaciones con un objeto de tipo OleDbCommand, en un escenario conectado, ¿que tipo de bloqueo se aplica por defecto?, ¿como lo puedo modificar sin emplear transacciones?.

4. ¿Como puedo implementar el bloqueo de tipo “el último gana”?

5. Cuando se emplean transacciones, ¿los niveles de aislamiento definen el tipo de bloqueo?

La verdad esq tengo un poco de “empanada mental” con estos estos conceptos. A ver si me lo puedes aclarar un poco.

Saludos y gracias!.

jtentor Argentina

Thursday, January 21, 2010 4:41 PM

jtentor

Alex,
Primero vamos a revisar dos conceptos: 1) Bloqueo se utiliza para que otro proceso (usuario) no acceda al mismo registro mientras yo lo estoy haciendo. 2) Transacción se utilizar para garantizar la integridad de varios accesos.
Repasemos, en un proceso de venta necesito bloquear el registro que estoy vendiendo cuando voy a modificar la cantidad en existencia (stock), sino ocurre que dos procesos leen el valor actual (por ejemplo 10 unidades) y cada proceso hace su venta y luego proceden a regrabar el nuevo stock, con lo cual queda el último valor, dado que uno de ellos vendió 2 unidades con lo que graba el valor 8 y el otro proceso vendió 3 unidades y graba el valor 7. La solución es bloquear el registro cuando se lo lee, actualizarlo y luego liberarlo, pero esto puede ser un verdadero problema si se trata de un supermercado con muchos puestos de venta, imaginate que todas las cajas tienen que esperar a que una de ellas libere un registro para poder continuar. La otra solución que yo sugiero es codificar un procedimiento almacenado en el motor de la base de datos que reciba dos parámetros uno de ellos el código de artículo y el otro la cantidad a descontar, de este modo cada proceso invoca al procedimiento almecenado con estos dos valores y la instrucción en SQL sería algo como UPDATE articulos SET StockActual = StockActual - @Cantidad WHERE CodigoArticulo = @Codigo, de este modo nos aseguramos que cada acceso se "encola" en el proceso del motor de la base de datos y nuestra vida como programadores es más facil.
El otro tema, la transacción, se utiliza cuando se deben actualizar dos o más tablas o registros de la misma tabla; por ejemplo descontar un valor de un registro de la tabla cuentas y sumar ese mismo valor en otro registro de la misma tabla cuentas. En este caso debemos asegurar la integridad de la operación por eso es necesario una transacción que envuelva las dos operaciones (la resta del 1er registro y la suma en el 2do registro). Las transacción son manejadas por el motor de la base de datos como una sola operación, de manera que si no puede realizarse se deshace lo que ya se hizo y todo queda como si no hubiese pasado nada.
El concepto de bloqueo pesimista es aplicable en tablas planas o archivos que no están controlados por el motor de base de datos, lo mismo pasa con el último gana; dado que en un motor de base de datos es ese proceso el que se encarga de encolar los accesos al mismo registor uno detrás de otro, por eso es importante pensar la sentencia que se utiliza (analizar el código de actualización de stock que puse más adelante)
El nivel de aislamiento también es propio del motor de base de datos y se refier a en qué momento graba efectivamente en las tablas de la base de datos, recordemos que un motor de base de datos utilizar un registro de transacciones donde se van llevando a cabo las operaciones y luego las vuelca definitivamente en las tablas, esto mejora la performance pero implica un riesgo dado que el registro de transacciones puede ser muy grande o permanecer demasiado tiempo en memoria y si se corta la energia del servidor esas transacciones no fueron volcadas en las tablas, por eso un servidor de base de datos debe tener una fuente de energia suplementaria y un mecanismo de cierre de acceso cuando se detecta la ausencia de energia, de este modo no se podrá arruinar la base de datos.
El DataAdapter es una simulación (en ram) de la base de datos, de manera que es muy complicado implementar bloqueos (es posible pero muy laborioso) yo prefiero organizar acceso sencillos. Un objeto como SqlCommnad o OleDbCommand en modo conectado utiliza el modo de bloqueo que tiene el motor de base de datos, esto es encolar los accesos si el registro está bloqueado hasta que se libere, por supuesto siempre que se desee actualizar el registro, los INSERT no traen problemas y las lecturas en general se realizan pero pueden traer información "sucia" con esto quiero decir que a lo mejor un proceso lee un registor pero inmediatamente otro proceso lo modifica de manera que el primer proceso tiene en ram información que no se corresponde con lo que está en la base de datos, este último problema se puede solucionar con un campo del tipo TimeStamp en las tablas (al menos las críticas), este tipo de campos tienen la capacidad de actualizarse con un valor de fecha y hora cada vez que se modifica el registro; de manera que cuando un proceso lee el registro obtiene el valor del TimeStamp y cuando posteriormente quiere actualizarlo debería utilizar una forma de verificar que el TimeStamp no ha cambiado, por supuesto para eso hay que codificar sentencias SQL complejas o Procedimientos Almacenados en el motor de la base de datos.

Alex Spain

Friday, January 22, 2010 3:29 AM

Alex

Alex

Hola Julio,
muchísimas gracias por tú extenso comentario y atención!.

Lo estudiaré con detenimiento.

Saludos!.

Martin Argentina

Wednesday, January 27, 2010 3:32 AM

Martin

Hola profe, tengo una consulta acerca de los DataSet. Como hago para guardar datos desde un DataSet a una base de datos de Sql.
El codigo que tengo para crear el DataSet y lcargarlo con datos de una tabla es:

        string strCon = "data source=(local)\\SQLEXPRESS; initial catalog=GENERADORES; integrated security=true";
        SqlConnection con = new SqlConnection(strCon);
        SqlDataAdapter daEmpleado = new SqlDataAdapter("select * from empleados",con);

        DataSet dsEmpleado = new DataSet();
        daEmpleado.FillSchema(dsEmpleado, SchemaType.Source, "empleados");
        daEmpleado.Fill(dsEmpleado, "empleados");

Hasta aqui funciona bien, pero no se como hacer que los datos que agrego se guarden en la base de datos.
Encontre un ej de codigo que me parecio bueno pero no actualiza la base de datos.El mismo crea un DataTable
que contiene los datos de la tabla Empleado y a la que agrego una fila; esta fila se agregaefectivamente al DataTable
dtEmpleados pero no actualiza la ase de datos.

        DataTable dtEmpleados;
        dtEmpleados = dsEmpleado.Tables["empleados"];
        DataRow newRow = dtEmpleados.NewRow();
        newRow["clave"] = "130";
        newRow["apynom"] = "jose gomez";
        dtEmpleados.Rows.Add(newRow);

Bueno, espero pueda ayudarme profe, de antemano gracias.

jtentor Argentina

Wednesday, January 27, 2010 1:35 PM

jtentor

El DataSet es un objeto espectacular que representa en memoria una parte de la base de datos, en general aquella que se obtiene con la sentencia SELECT asociada al DataSet.
Así como se puede poner una sentencia SELECT, también se puede asociar sentencias UPDATE, DELETE e INSERT; que permitirán realizar el CRUD (Create, Read, Update and Delete) completo de una tabla.
Si te fijas en todas las propiedades del DataSet vas a encontrar todo esto y los métodos que deben invocarse para que todo lo que se hizo en memoria se vuelque a la base de datos. Particularmente no me gusta utilizar esta modalidad porque puede haber conflictos con otros procesos que acceden a la misma información.

Por ejemplo podríamos desarrollar una aplicacion de comercio electronico donde se confecciona la factura en un DataSet y cuando el usuario selección la opción grabar, mandamos a actualizar el DataSet completo; que pasa con el stock de cada árticulo vendido si otros procesos (usuarios) accedian concurrentemente a comprar los mismos artículos?

Más allá de mi comentario sobre la complejidad de algunas aplicaciones o partes de ellas, opino que el DataSet es una herramienta fabulosa que puede y debe utilizarse, por supuesto hay que analizar las situaciones en las que se puede utilizar los métodos propios del objeto o invocar un método propio que realice el trabajo, para ello se puede saber que filas (rows) del DataSet o DataTable (un DataSet puede tener más de una tabla, para eso sirve el DataTable) fueron modificadas, cuales son nuevas y cuales fueron borradas; todo esto en memoria y nuestro método vuelca estos cambios en la base de datos uno a uno o en una transacción que garantice la integridad de la información.

Saludos.

Martin Argentina

Wednesday, February 17, 2010 8:22 PM

Martin

Hola profe, como le va, disculpe la molestia pero tengo una duda coceptual acerca de C#.
Mi duda es porque dice "partial" en lalinea de codigo siguiente:

public partial class pagina_Default3 : System.Web.UI.Page

Bueno estare esperando que me aclare esta duda, de antemano gracias.

Saludos.

jtentor Argentina

Wednesday, February 17, 2010 8:31 PM

jtentor

Martín, "partial" es un clasificador que tiene C#, en realidad lo incorpora el Microsoft Framework 2.0 y te permite esciribr el código que implementa una clase en varios archivos, de otro modo tendrías que escribir en un único archivo la parte que corresponde a la interfáz de usuario (la que tiene HTML) y en el mismo archivo la parte de lógica o soporte de la interfaz de usuario.
Es posible escribir un clase por ejemplo Cliente en varios archivos. Cliente_Logica.cs, Cliente_AccesoDatos.cs, Cliente_Validaciones.cs, ... en todas ellas tendrías que poner el calificador "partial" y el compilador junta todo y lo compila. Esto favorece el desarrollo en equipos de trabajo porque una persona puede utilizar una herramienta como ExpresionBlend o ExpresionWeb para trabajar en la interfáz y otra persona al mismo tiempo codifica en el archivo de soporte para dicha interfaz.

Espero que sirva.

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading