Arquitectura - Definición de un DataHelper, Parte 3.

Hola. ¿Qué tal?...

Hemos visto ya las dos primeras partes de la propuesta de DataHelper. Bien, pues continuando con la construcción de lo que será la plataforma que sustente el acceso a datos dentro de la definición de esta arquitectura terminaremos el DataHelper con los métodos transaccionales.

Los métodos transaccionales, definidos así porque serán los que permitan enlazar ejecuciones hacia la base de datos dentro de los componentes de negociación. Estas ejecuciones no son nada nuevo en las aplicaciones de mediana y gran escala, donde en ocasiones nos vemos en la necesidad de afectar registros en dos o más tablas a la vez y con dependencia entre ellos, esto es, que deban afectarse todos los registros involucrados en la transacción de forma correcta y que tengan la capacidad de dejar la base de datos como estaba al inicio de la transacción en caso de un error. Para lograr esto, debemos valernos de las transacciones de la base de datos, con lo que se asegura la integridad de la información evitando dejar huecos o datos inútiles en la base de datos a consecuencia de un error en alguno de los pasos de ejecución hacia la base de datos si alguna de las dependencias de datos no se aseguran en la afectación de datos que realice la aplicación.

Los métodos que describiré a continuación son meramente exclusivos para componentes de negociación (BC), ya que la administración de transacciones son por definición exclusivas del BC. Un BC en definición es el utilizado para negociar las transacciones de entre los componentes de la aplicación, ya sea de los propios BC’s o entre un BC y un DAC o entre DAC’s, puede ser cualquier combinación que sea requerida para llevar a cabo tareas propias del proceso de la aplicación, más adelante en otra publicación hablaré de estos componentes, ejemplificando su uso.

Bueno, comencemos con los métodos de una buena vez, iniciando por el proveedor de transacciones comunes, este método se dará a la tarea de proveer una transacción para ser utilizada de forma única en una secuencia de transacciones. Veamos pues:

VB

Protected Function GetCommonTransaction() As SqlTransaction

    If Not IsValidConnection() Then

        Return Nothing

    End If

 

    If cnnHelper.State = ConnectionState.Closed Then

        cnnHelper.Open()

    End If

 

    Return cnnHelper.BeginTransaction()

End Function

C#

protected SqlTransaction GetCommonTransaction()

{

    if (!IsValidConnection())

        return null;

 

    if (cnnHelper.State == ConnectionState.Closed)

        cnnHelper.Open();

 

    return cnnHelper.BeginTransaction();

}

Como podemos ver, este método solo se encarga de devolver un SqlTransaction, para esto requiere que la conexión esté abierta y sea válida, por lo que se hacen las validaciones correspondientes para poder devolver una transacción iniciada, es importante advertir que este método lo utilizan los Business Components, luego veremos de qué manera.

Continuando, con los otros métodos, bueno, no habrá mucho que decir pues son los mismos que los descritos en la segunda publicación de esta propuesta y solo se diferencian por un parámetro adicional, que es la transacción que se utiliza para enlazar las instrucciones.

Básicamente tendrá un comportamiento abierto, mismo que tendrá que ser administrado por un componente de negocio, esto es, que las ejecuciones de las instrucciones no cierran la conexión dado que podrían estar en una secuencia de instrucciones bajo la misma transacción. Cerrar la conexión propiciaría la interrupción de la transacción. Como hemos visto, el DataHelper ahora contiene un método que proporciona una transacción común,  en la que hemos abierto la conexión para habilitar la transacción, así pues, dejamos que los componentes de negociación realicen su función administrando las decisiones en la ejecución de transacciones, y serán estos los que se encarguen de terminar la secuencia de instrucciones y en su defecto, comprometer la transacción (commit).

Así pues, aquí les muestro los métodos transaccionales:

VB

Protected Function ExecuteTransaction(ByVal cmd As SqlCommand, _

                                      ByVal trn As SqlTransaction) _

                                      As Integer

    mErrorNumber = 0

    Dim cnn As SqlConnection = trn.Connection

 

    If cnn.State = ConnectionState.Closed Then

        Return -1

    End If

 

    Dim res As Integer

 

    Try

        cmd.Connection = cnn

        cmd.Transaction = trn

        res = cmd.ExecuteNonQuery()

        If res > 0 Then

            mMessageHelper = _

                String.Format( _

                "La operación se realizó con éxito." & _

                "\nSe afectaron {0} filas", _

                res.ToString())

        Else

            mMessageHelper = "La operación no afectó ninguna fila"

        End If

    Catch ex As SqlException

        trn.Rollback()

        If Not cnn Is Nothing AndAlso _

               cnn.State = ConnectionState.Open Then

            cnn.Close()

        End If

 

        mErrorNumber = ex.Number

        mMessageHelper = ex.Message

        res = -1

    End Try

 

    Return res

End Function

Protected Function GetQuery(ByVal cmd As SqlCommand, _

                            ByVal trn As SqlTransaction) _

                            As DataTable

    mErrorNumber = 0

    Dim da As SqlDataAdapter

    Dim dt As DataTable = New DataTable()

 

    If Not trn.Connection Is Nothing OrElse _

           trn.Connection.State = ConnectionState.Closed Then

        Return New DataTable()

    End If

 

    cmd.Connection = trn.Connection

    cmd.Transaction = trn

 

    da = New SqlDataAdapter(cmd)

 

    Try

        da.Fill(dt)

        mMessageHelper = "La operación resultó un éxito"

    Catch ex As SqlException

        mErrorNumber = ex.Number

        mMessageHelper = ex.Message

    End Try

 

    Return dt

End Function

Protected Function GetRecord(ByVal cmd As SqlCommand, _

                             ByVal trn As SqlTransaction) _

                             As DataRow

    Dim dt As DataTable = GetQuery(cmd, trn)

    If dt.Rows.Count > 0 Then

        Return dt.Rows(0)

    Else

        Return Nothing

    End If

End Function

Protected Function GetScalar(ByVal cmd As SqlCommand, _

                             ByVal trn As SqlTransaction) _

                             As Object

    mErrorNumber = 0

    Dim cnn As SqlConnection = trn.Connection

 

    Try

        cmd.Connection = cnn

        cmd.Transaction = trn

        Return cmd.ExecuteScalar()

    Catch ex As SqlException

        mErrorNumber = ex.Number

        mMessageHelper = ex.Message

        Return Nothing

    Catch ex As InvalidCastException

        mErrorNumber = -2

        mMessageHelper = ex.Message

        Return Nothing

    End Try

End Function

C#

protected int ExecuteTransaction(SqlCommand cmd, SqlTransaction trn)

{

    errorNumber = 0;

    SqlConnection cnn = trn.Connection;

    if (cnn.State == ConnectionState.Closed)

        return -1;

 

    int res;

    try

    {

        cmd.Connection = cnn;

        cmd.Transaction = trn;

        res = cmd.ExecuteNonQuery();

        if (res > 0)

            messageHelper =

                string.Format(

                "La operación se realizó con éxito." +

                "\nSe afectaron {0} filas",

                res.ToString());

        else

            messageHelper = "La operación no afectó ninguna fila";

    }

    catch (SqlException ex)

    {

        trn.Rollback();

        if (cnn != null && cnn.State == ConnectionState.Open)

            cnn.Close();

        errorNumber = ex.Number;

        messageHelper = ex.Message;

        res = -1;

    }

    return res;

}

protected DataTable GetQuery(SqlCommand cmd, SqlTransaction trn)

{

    errorNumber = 0;

    SqlDataAdapter da;

    DataTable dt = new DataTable();

 

    if (trn.Connection == null ||

        trn.Connection.State == ConnectionState.Closed)

        return new DataTable();

 

    cmd.Connection = trn.Connection;

    cmd.Transaction = trn;

 

    da = new SqlDataAdapter(cmd);

 

    try

    {

        da.Fill(dt);

        messageHelper = "La operación resultó un éxito";

    }

    catch (SqlException ex)

    {

        errorNumber = ex.Number;

        messageHelper = ex.Message;

    }

    return dt;

}

protected DataRow GetRecord(SqlCommand cmd, SqlTransaction trn)

{

    DataTable dt = GetQuery(cmd, trn);

    if (dt.Rows.Count > 0)

        return dt.Rows[0];

    else

        return null;

}

protected object GetScalar(SqlCommand cmd, SqlTransaction trn)

{

    errorNumber = 0;

    SqlConnection cnn = trn.Connection;

    try

    {

        cmd.Connection = cnn;

        cmd.Transaction = trn;

        return cmd.ExecuteScalar();

    }

    catch (SqlException ex)

    {

        errorNumber = ex.Number;

        messageHelper = ex.Message;

        return null;

    }

    catch (InvalidCastException ex)

    {

        errorNumber = -2;

        messageHelper = ex.Message;

        return null;

    }

}

Lo más interesante de esto, pues, en general es que al SqlCommand que se utiliza, además de pasarle la SqlConnetion (cnn) también se le pasa la SqlTransacction (trn) que tomamos de un nuevo parámetro que viene de la sobrecarga de los métodos iníciales. Así es, estamos sobrecargando los métodos originales con un nuevo parámetro, el cual será la transacción común cuando le llegue el momento de utilizarlo.

Por otra parte, cabe hacer notar que el método ExecuteTransaction no cierra la conexión, y lo único que hace es controlar el error, aquí es donde se deshace toda la transacción por el error en alguna instrucción. El DataHelper solo administra los errores, y en cuanto surja alguno, pues se hará el respectivo llamado de Rollback de la transacción común, con esto, se está restituyendo la base de datos al estado inicial y a la vez se destruirá la SqlConnection que se traía en la transacción común, que nos será útil para saber si la transacción hizo rollback o no.

Bien, con esto terminamos con la definición del DataHelper, aunque en su versión más básica, es útil para diferentes tareas. Lo siguiente será mostrar su uso de manera práctica.

También pueden leer la primera parte y la segunda parte del documento en:
http://msmvps.com/blogs/otelis/archive/2009/03/11/arquitectura-definici-243-n-de-un-datahelper-parte-1.aspx

http://msmvps.com/blogs/otelis/archive/2009/03/15/arquitectura-definici-243-n-de-un-datahelper-parte-2.aspx

El código de descarga lo encuentran en:
http://code.msdn.microsoft.com/datahelper

Saludos…

Octavio Telis

Published Tue, Aug 25 2009 14:45 by Octavio Telis Aynés

Comments

# re: Arquitectura - Definición de un DataHelper, Parte 3.

Saturday, September 05, 2009 11:35 AM by el pollo lopez

Maestro eres toda una eminencia

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above:  
Powered by Community Server (Commercial Edition), by Telligent Systems