miércoles, 6 de diciembre de 2017

App desktop: editar app.config en tiempo de ejecución

Para un proyecto, se nos pidió implementar la funcionalidad de click- once. Esta aplicación operaba para 4 regiones: Córdoba, Buenos Aires, Mendoza y Tucumán. Para saber cuál era la  región con la que se debía operar, la misma utilizaba un archivo de configuración (.ini) que establecía el entorno de la aplicación, indicando las conexiones a DBs y varios parámetros más utilizados para determinadas funcionalidades dentro de la misma.

Cada archivo (.ini) tenía diferentes variables configuradas dependiendo de la región en la que se operaba, para lo cual utilizaba accesos con parámetros de entrada.

¿Qué se quería resolver?

Lo que se necesitaba era eliminar los accesos directos ya que, por medio de click- once, no se podía generar un ejecutable parametrizado por cada región donde se debía instalar la aplicación. Dicha región, debía configurarse de forma tal que permitiera que el usuario no la vuelva a seleccionar cada vez que reiniciaba la aplicación. De esta forma, era necesario que la configuración se guardara para los inicios posteriores; pudiendo cambiarla las veces que fuera requerido. 

Si bien el problema parece sencillo, siempre hay que tener en cuenta el entorno donde se debe aplicar la solución. No es lo mismo una aplicación web que una de escritorio, más aun cuando hablamos de tecnología antigua.
Sólo a modo de resumen, estos son los principales limitantes que tuvimos que tener presente al momento de resolver este acertijo:

- Framework 2.0 (si, todavía hay aplicaciones que los usan)
- Lenguaje: Visual Basic .NET (VB6 con traje)
- IDE: Visual Studio 2005 SP1 (podría ser peor, Fox por ejemplo)
- No respeta ninguna arquitectura. 
- No se encontraba optimizado para test unitarios (si compila funciona).
- Aplicación WinForm

La solución

Se propuso crear un selector de regiones utilizando el app.config, generando una clave que se edite en tiempo de ejecución para que guarde la región seleccionada.

La búsqueda no fue sencilla. Navegando por internet encontramos muchas soluciones, pero ninguna de ellas era apta para nuestro problema debido a la tecnología con la que estábamos lidiando. Pero finalmente, la solución apareció.

Se agregó un nuevo proyecto en la solución, siendo necesario que estuviera referenciado en todos los proyectos de la aplicación  y se creó la clase cAppConfig.vb, que contenía el siguiente código: 

Imports System.Configuration
Imports System.Reflection
Public Class cAppConfig
    Private _config As Configuration
    Private _settings As AppSettingsSection

    Public Function GetProperty(ByVal propertyName As String) As String
        Return _settings.Settings.Item(propertyName).Value
    End Function

    Public Sub SetProperty(ByVal propertyName As String, ByVal propertyValue As String)
        If Not IsNothing(_settings.Settings.Item(propertyName).Value) Then
            _settings.Settings.Item(propertyName).Value = propertyValue
            _config.Save(ConfigurationSaveMode.Modified)
        Else
            _settings.Settings.Add(propertyName, propertyValue)
            _config.Save(ConfigurationSaveMode.Modified)
        End If
    End Sub

    Public Sub New()
        _config = ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location)
        _settings = _config.AppSettings
    End Sub
End Class

En esta clase se encontraba la función GetProperty, que obtenía el valor de la clave del app. SetProperty permitía asignar el valor a la clave si ésta existía, en el caso contrario la creaba con el valor asignado por parámetro. Luego permitía guardar los cambios.
(_config.Save(ConfigurationSaveMode.Modified)).

Y por último, se encontraba el constructor de la clase. Este guardaba en una variable del tipo AppSettingsSection el app.config.

_config = ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location)
 _settings = _config.AppSettings

En el mismo proyecto se agregó además otra clase llamada cRegion con el siguiente código:

Imports System.Configuration
Public Class cRegion

    Private Shared mRegion As cRegion

    Private Sub New()
        Try
            If Not IsNothing(System.Configuration.ConfigurationManager.AppSettings("RegionDefault")) Then
                RegionDefault = System.Configuration.ConfigurationManager.AppSettings("RegionDefault").ToString
            Else
                Dim cConfig As New cAppConfig
                cConfig.SetProperty("RegionDefault", "")
                RegionDefault = System.Configuration.ConfigurationManager.AppSettings("RegionDefault").ToString
            End If
        Catch ex As Exception
            MsgBox(ex.StackTrace.ToString & "cRegion")
        End Try
    End Sub

    Private cRegion As String
    Public Property RegionDefault() As String
        Get
            Return cRegion
        End Get
        Set(ByVal value As String)
            cRegion = value
        End Set
    End Property

    Public Shared Property Instance() As cRegion
        Get
            If IsNothing(mRegion) Then
                mRegion = New cRegion
            End If

            Return mRegion
        End Get
        Set(ByVal value As cRegion)

        End Set

    End Property

En esta clase el constructor permitía obtener la región configurada en el app.config; si no existía, la creaba y le asignaba un valor por defecto, en este caso un string vacío. 

Además, se implementó el patrón Singleton Instance(), utilizándose  una única instancia de dicha clase.

Se creó también una propiedad RegionDefault para obtener la región y poder cambiar su valor.

En la clase que inicializaba la aplicación (frmInicio) se creó un método para cargar la región (CargarRegion()), el cual era llamado desde el constructor por defecto.

Public Class frmInicio
Inherits System.Windows.Forms.Form

    Public Sub New()
        MyBase.New()
        Try
       'ESTO ES COMO SE OBTENIA LA REGION ANTERIORMENTE A TRAVEZ DEL PARAMETRO DEL ACCESO DIRECTO'

            'Dim sCd = Environment.GetCommandLineArgs(1)
            'Dim sCd = "Sys.xml"

          CargarRegion()

            'This call is required by the Windows Form Designer.
            InitializeComponent()
            'Add any initialization after the InitializeComponent() call
        Catch ex As Exception
            MsgBox("Se debe ingresar el parametro de inicio")
            Me.Dispose()
            Me.Close()
        End Try
    End Sub
    Private Sub CargarRegion(Optional ByVal sregion As Integer = 0)
        Dim sXml As String
        Try
            If (cRegion.Instance.RegionDefault) = vbNullString Then
                Dim frmRegion As New frmRegion
                frmRegion.ShowDialog()
            End If
        Catch ex As Exception
            oEntorno.Trazar(ex.ToString & ex.StackTrace, "SUA-CargarRegion")
        End Try
    End Sub


El método “cargar región”, es el que se encargaba de verificar si en el app.config la clave "RegionDefault" tenía un valor asociado. Si no contenía ninguna región cargada, creaba la instancia de un nuevo formulario (frmRegion) que le permitiera al usuario elegir entre las 4 regiones disponibles (Buenos Aires, Córdoba, Tucumán y Mendoza).

En el formulario frmRegion, una vez confirmada la región, se procedía a guardar el valor en el app.config:

Public Class frmRegion
 …
    ''' <summary>
    ''' Graba en nombre Archivo de Configuracion Según la Región Seleccionada
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub AsignarRegion()
        Dim sRegion As String
        Dim sAppConfig As New cAppConfig
        Select Case ObtenerRegion()
            Case "BA"
                sRegion = "SysBA.ini"
            Case "CBA"
                sRegion = "SysCBA.ini"
            Case "MZA"
                sRegion = "SysMZA.ini"
            Case "TUC"
                sRegion = "SysTUC.ini"
            Case Else
                sRegion = ""
        End Select
        sAppConfig.SetProperty("RegionDefault", sRegion)
        cRegion.Instance.RegionDefault = sAppConfig.GetProperty("RegionDefault")
    End Sub


End Class

Una vez realizada la elección de la región, solo bastaba con llamar a la función que devolvía la ubicación del archivo de configuración para la región correspondiente: cRegion.Instance.ObtenerRegion

Si bien esta no es la única resolución, esperamos que les sirva y que nuestro aporte pueda ayudarlos a resolver su problema o, al menos, a orientarlos en el camino de diseñar e implementar una solución a la medida de sus necesidades.

Autores: 

Marcos Guaymas 
Técnico superior en Sistemas.

Marco Paredes Reccio 
Técnico en computación e Informática.

miércoles, 22 de noviembre de 2017

¿Por qué deberías usar DataTables?

En esta oportunidad hablaremos sobre Datatables, una herramienta muy poderosa, que nos permite ahorrar  gran cantidad de tiempo a la hora de programar, al abstraernos de los detalles más técnicos de la implementación.

Pero, ¿qué es exactamente Datatables? Bueno, es un plug-in gratuito para el desarrollo de aplicaciones web que se monta sobre jQuery (otro reconocido componente del mundo web). Es una herramienta extremadamente flexible que permite la construcción de grillas de manera rápida y sencilla, invirtiendo así un esfuerzo de desarrollo bastante menor.


En nuestro proyecto surgió la necesidad de implementar una grilla en pantalla que, principalmente, sea exportable a Excel. Además, la misma debía ser muy flexible y dar la posibilidad al usuario de poner filtros y ordenar los datos según diferentes criterios.
Luego de analizar varias opciones que existen en el mercado, entre las cuales se destacaba jqGrid, decidimos usar Datatables.
A continuación mencionamos algunos de los principales tópicos que consideramos en su momento para tomar nuestra decisión y terminar optando por el uso de este framework:

Pros
  • Una vez implementada, es muy simple replicarla en el resto de la aplicación.
  • Es sencillo ampliar y/o modificar el plug-in ya que es de código abierto y existe una amplia comunidad que lo mantiene y evoluciona constantemente.
  • Del lado del frontend, la cantidad de código necesaria para dejarla operativa es ínfima en comparación a otras soluciones que hemos evaluado.
  • Incluye la posibilidad de exportar a Excel, lo cual era un requerimiento indispensable en nuestro proyecto. La exportación a Excel se puede lograr con un mínimo esfuerzo y sin necesidad de integrar otro componente adicional.
  • Posee múltiples tipos de paginación disponibles out of the box.
  • Brinda la posibilidad de configurar diversos tipos filtros para las diferentes columnas.
  • Posee soporte para internacionalización.
  • Permite configurar múltiples opciones de orígenes de datos.
  • Es customizable mediante CSS.
  • Existe mucha documentación disponible en Internet, lo que facilita enormemente su implementación.

Contras
  • Al abstraerse de los detalles de su implementación, como con todo componente, estamos delegando control al plug-in, pero en realidad sucede lo mismo con todos los frameworks y, de hecho, es algo que deseamos ya que nos permite enfocarnos más en los temas funcionales y no tanto en los técnicos.
  • El punto crucial es si esta abstracción se encuentra en la justa medida que nos permita, por un lado, enfocarnos en la funcionalidad a construir y, por el otro, personalizar el componente de acuerdo a los requerimientos proporcionados por nuestro cliente.

Beneficios
Estamos muy contentos con DataTables.Entre los múltiples beneficios que obtuvimos podemos mencionar:
  • Se redujo el tiempo de desarrollo.
  • No tuvimos inconvenientes de performance ni bugs con las grillas.
  • Las grillas son fáciles de mantener.
  • Funcionan correctamente entre los varios navegadores del mercado.
  • El cliente quedó muy conforme con su visualización.
  • Su uso es muy intuitivo.

Actualmente tenemos planificado hacer unas modificaciones al plug-in para poder modularizar nuestros desarrollos particulares sobre el componente y, en un futuro, subirlo a NuGet para que esté disponible para otros proyectos específicos y para la comunidad en general.

En el sitio oficial hay gran variedad de documentación que los desarrollares y arquitectos podrán consultar para obtener más referencias sobre este plug-in, con una mantención muy activa, y en donde, además, se encuentran muchos ejemplos sobre su utilización. Por ejemplo, este es uno de ellos: https://datatables.net/examples/basic_init/zero_configuration.html

Links útiles:

Referencias:

Autores:
Pablo Bianco 
Video game & .Net developer 
Ingresó a Baufest a través del programa de entrenamiento intensivo (PEI). Inició sus actividades en un proyecto de desarrollo en el  cual utiliza varias tecnologías modernas.

Julián Haeberli 
Actualmente se desempeña como Technical Expert en un proyecto de soporte de aplicaciones para un cliente nacional. A lo largo de su carrera dentro de Baufest se certificó en varias tecnologías y logró variados reconocimientos a su esfuerzo y labor.