Arquitectura – Definición de un DataHelper, Parte 2

Hola ¿Qué tal?

Continuando con la segunda parte de la definición de un DataHelper, dentro de la propuesta de arquitectura de software, continuaremos con la definición de los métodos funcionales.

En general la funcionalidad del Helper se contruye alrededor de las necesidades de los Data Access Components que definiremos más adelante y que básicamente son Insertar, Modificar, Eliminar y Consultar registros en la base de datos. Los DACs tomarán la funcionalidad provista por el Helper a través de los métodos funcionales, estos métodos se definen con alcance protegido y esto debido a que el Helper es una clase abstracta y no todo será expuesto al exterior de los Data Access Components (DACs).

Iniciemos por el método que utilizaremos para Insertar, Modificar y Eliminar registros. Este método usará una transacción aunque dado que en teoría será única la ejecución de la instrucción podría no necesitarse, sin embargo la utilizamos por si la instrucción viene de un Stored Procedure que ejecute varias instrucciones que requieran englobarse en una transacción. El método es del tipo integer y devuelve la cantidad de filas afectadas. Aquí está el método:

VB

Protected Function ExecuteTransaction(ByVal cmd As SqlCommand) As Integer

    mErrorNumber = 0

    If (Not IsValidConnection()) Then

        Return -1

    End If

 

    Dim trn As SqlTransaction = Nothing

    Dim res As Integer

 

    cmd.Connection = cnnHelper

    Using cnnHelper

        Try

            cnnHelper.Open()

            trn = cnnHelper.BeginTransaction()

            cmd.Transaction = trn

            res = cmd.ExecuteNonQuery()

            trn.Commit()

            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()

            mErrorNumber = ex.Number

            mMessageHelper = ex.Message

            res = -1

        End Try

    End Using

    Return res

End Function

C#

protected int ExecuteTransaction(SqlCommand cmd)

{

    errorNumber = 0;

    if (!IsValidConnection())

        return -1;

 

    SqlTransaction trn = null;

    int res;

    cmd.Connection = cnnHelper;

    using (cnnHelper)

    {

        try

        {

            cnnHelper.Open();

            trn = cnnHelper.BeginTransaction();

            cmd.Transaction = trn;

            res = cmd.ExecuteNonQuery();

            trn.Commit();

            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();

            errorNumber = ex.Number;

            messageHelper = ex.Message;

            res = -1;

        }

    }

    return res;

}

Como podemos observar, el método recibe un parámetro de tipo SqlCommand. Dentro de la ejecución del método; primeramente se valida que la conexión se pueda utilizar, seguido de la definición de algunas variables, entre ellas la de transacción, pero más importante; se asignará la conexión al SqlCommand que se ha pasado como parámetro. Este SqlCommand viene con las definiciones necesarias para su ejecución pero completamente desconectado, ya que el Helper se encarga de manejar la conexión, todos los parámetros de este y de los demás métodos del Helper que requieran de una conexión no necesitarán incluirla pues el Helper ser las proveerá. Seguido en la ejecución, se engloba el uso de la conexión utilizando la instrucción Using, de esta manera liberamos los recursos de la conexión una vez utilizada. Tenemos una instrucción try – cath para manejar los errores que emerjan por cualquier causa en del servidor o por la ejecución de instrucciones con errores, es conveniente comentar que al controlar estos errores en el helper no se propagarán hacia niveles superiores por la seguridad en la ejecución de la aplicación, sin embargo, tendremos varias maneras de saber qué sucedió, una es por el número de error, donde siempre será cero para los casos en que no se detecten errores y un valor distinto de cero en caso de que los haya, otra manera es la descripción del error, que estará expuesta en la propiedad MessageHelper. El resto ya es práctica conocida del uso de SqlCommand.

Continuando con la funcionalidad del Helper, toca el turno a la ejecución de instrucciones SQL de forma directa, veamos, una instrucción que pueda ejecutarse a través de un SqlCommand pero que no requiera de una transacción, esto es, una simple instrucción SQL. Se puede utilizar esta instrucción para Insertar, Actualizar y Borrar de igual manera que en la anterior, lo recomendaría solo en el caso en que la instrucción SQL sea única y no implique otros movimientos, esto es, un simple Insert o Update o Delete. El caso anterior es muy parecido a este pero sin la transacción. Veamos pues:

VB

Protected Function ExecuteQuery(ByVal cmd As SqlCommand) As Integer

    mErrorNumber = 0

    Dim res As Integer

 

    If (Not IsValidConnection()) Then

        Return -1

    End If

 

    cmd.Connection = cnnHelper

    Using (cnnHelper)

        Try

            cnnHelper.Open()

            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

            mErrorNumber = ex.Number

            mMessageHelper = ex.Message

            res = -1

        End Try

    End Using

 

    Return res

End Function

 

C#

protected int ExecuteQuery(SqlCommand cmd)

{

    errorNumber = 0;

    int res;

    if (!IsValidConnection())

        return -1;

 

    cmd.Connection = cnnHelper;

 

    using (cnnHelper)

    {

        try

        {

            cnnHelper.Open();

            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)

        {

            errorNumber = ex.Number;

            messageHelper = ex.Message;

            res = -1;

        }

    }

    return res;

}

No hay mucho que explicar, solo que es el mismo método que el anterior solo que no utiliza un SqlTransaction.

Continuando con los métodos adicionales, toca el turno a la consulta de datos. Tenemos dos tipos de consultas, las que devuelven un único conjunto de resultados y las que devuelven varios conjuntos de resultados. Para las consultas que devuelven un solo conjunto de resultados, definiremos un método que ejecute la consulta y que devuelva el conjunto de resultados en un contenedor de datos, que en nuestro caso será un DataTable. Veamos pues este método:

VB

Protected Function GetQuery(ByVal cmd As SqlCommand) As DataTable

    mErrorNumber = 0

    Dim da As SqlDataAdapter

    Dim dt As DataTable = New DataTable()

 

    If (Not IsValidConnection()) Then

        Return dt

    End If

 

    cmd.Connection = cnnHelper

    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

 

C#

protected DataTable GetQuery(SqlCommand cmd)

{

    errorNumber = 0;

    SqlDataAdapter da;

    DataTable dt = new DataTable();

 

    if (!IsValidConnection())

        return dt;

 

    cmd.Connection = cnnHelper;

    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;

}

En este método utilizamos un SqlDataAdapter para poder utilizar el método Fill que se encarga de llenar el DataTable con los datos resultantes de la consulta. Como observación, solo basta asignar la conexión al data adapter una vez que se ha inicializado con el SqlCommand que vienen como parámetro de entrada, no es necesario abrir la conexión ya que el método Fill tiene la característica de que si la conexión no está abierta, la abre y ejecuta la consulta, llena el DataTable y terminando cierra la conexión. Si la conexión estuviese abierta, el método Fill la dejaría abierta. Bien, pues continuemos con lo siguiente.

El método anterior solo tiene la capacidad de devolver un DataTable con el conjunto de resultados de una consulta, sin embargo, hay ocasiones en que se ejecutan consultas que devuelven más de un conjunto de resultados y en esos casos el método anterior no sería de mucha utilidad, además de que no sería muy óptimo dividir una consulta única en varias para obtener todos los resultados por separado. Bien, para esta labor, dejaremos que Sql Server se encargue de todas las consultas de una vez y en nuestro Helper dejaremos que se encargue de devolver un único contenedor de resultados, este sería un contenedor de DataTables, o sea, un DataSet. Veamos cómo queda este método:

VB

Protected Function GetQueries(ByVal cmd As SqlCommand) As DataSet

    mErrorNumber = 0

    Dim da As SqlDataAdapter

    Dim ds As DataSet = New DataSet()

 

    If (Not IsValidConnection()) Then

        Return ds

    End If

 

    cmd.Connection = cnnHelper

    da = New SqlDataAdapter(cmd)

 

    Try

        da.Fill(ds)

        mMessageHelper = “La operación resultó un éxito”

    Catch ex As SqlException

        mErrorNumber = ex.Number

        mMessageHelper = ex.Message

    End Try

 

    Return ds

End Function

C#

protected DataSet GetQueries(SqlCommand cmd)

{

    errorNumber = 0;

    SqlDataAdapter da;

    DataSet ds = new DataSet();

 

    if (!IsValidConnection())

        return ds;

 

    cmd.Connection = cnnHelper;

    da = new SqlDataAdapter(cmd);

 

    try

    {

        da.Fill(ds);

        messageHelper = “La operación resultó un éxito”;

    }

    catch (SqlException ex)

    {

        errorNumber = ex.Number;

        messageHelper = ex.Message;

    }

    return ds;

}

Como podemos ver, este método es muy parecido al anterior, con la única diferencia de que en este utilizamos un DataSet en lugar de un DataTable, lo demás es exactamente lo mismo.

Los dos últimos métodos de este artículo son a los que llamo de información puntual, uno es para devolver un solo registro con los campos que se definan en la consulta, y el otro sería más al punto, pues se encarga de devolver un solo valor. Veamos aquí el método que devuelve un registro:

VB

Protected Function GetRecord(ByVal cmd As SqlCommand) As DataRow

    Dim dt As DataTable = GetQuery(cmd)

    If dt.Rows.Count > 0 Then

        Return dt.Rows(0)

    Else

        Return Nothing

    End If

End Function

C#

protected DataRow GetRecord(SqlCommand cmd)

{

    DataTable dt = GetQuery(cmd);

    if (dt.Rows.Count > 0)

        return dt.Rows[0];

    else

        return null;

}

Bien, pues no es mucho lo que se podría explicar de este método ya que aprovecha la funcionalidad de uno de los métodos definidos más arriba. Solo quiero advertir que se debe tener prudencia al implementar este método en la práctica, ya que por rapidez debe utilizarse con instrucciones SQL preparadas para devolver un solo registro. El método tomará el primer registro del DataTable resultante de la consulta, validando antes que tenga al menos un registro, y lo devolverá como un DataRow. Si no se tienen elementos, se devuelve null (Nothing en Visual Basic). Este método es útil para llenar los campos de un Data Access Component, como lo veremos más adelante.

VB

Protected Function GetScalar(ByVal cmd As SqlCommand) As Object

    mErrorNumber = 0

    If Not IsValidConnection() Then

        Return Nothing

    End If

 

    cmd.Connection = cnnHelper

 

    Using (cnnHelper)

        Try

            cnnHelper.Open()

            mMessageHelper = “La operación se completó con éxito”

            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 Using

End Function

 

C#

protected object GetScalar(SqlCommand cmd)

{

    errorNumber = 0;

    if (!IsValidConnection())

        return null;

 

    cmd.Connection = cnnHelper;

 

    using (cnnHelper)

    {

        try

        {

            cnnHelper.Open();

            messageHelper = “La operación se completó con éxito”;

            return cmd.ExecuteScalar();

        }

        catch (SqlException ex)

        {

            errorNumber = ex.Number;

            messageHelper = ex.Message;

            return null;

        }

        catch (InvalidCastException ex)

        {

            errorNumber = -2;

            messageHelper = ex.Message;

            return null;

        }

    }

}

 

Este método a pesar de verse un poco más extenso, solo se encarga de exponer el método ExecuteScalar del SqlCommand validando la conexión y manejando los errores.

Con estos métodos le damos al Helper la capacidad funcional completa, con ello se podrán hacer todas las operaciones que un Data Access Component requiere, sin embargo, tenemos los Business Components, que se encargan de administrar las transacciones a diferencia del Helper que solo administra la conexión, un BC siempre administra las transacciones, pero esos métodos los veremos en la siguiente parte de este documento, a los cuales les llamo… los métodos transaccionales.

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

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

Continuar con la siguiente parte:
http://msmvps.com/blogs/otelis/archive/2009/03/15/arquitectura-definici-243-n-de-un-datahelper-parte-3.aspx

Saludos…

Octavio Telis


 

One thought on “Arquitectura – Definición de un DataHelper, Parte 2”

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>