AndorraDotNet

Comunitat d'usuaris .NET del pais dels pirineus
¿No perteneces a AndorraDotNet? ¡Regístrate y sé un miembro!
Iniciar sesión | REGÍSTRATE AQUÍ | Ayuda
en Buscar

400 Bad Request

Dímelo con flores ;-)

January 2008 - Artículos

  • ¿Quieres conocer las tripas del MsgBox?

    El código del runtime de VB2008 ha sido liberado...

    ...esta noticia junto a la de la liberación del Framework ha corrido como la pólvora desde hace unos días. Así que los chicos del VBTeam nos han preparado un pequeño artículo para demostrar cómo activar esta depuración, y así poder llegar hasta las tripas de este maldito código generado en el valle de Mordor. Me he tomado la libertad de traducirlo 'al vuelo' así que espero que no se hayan 'colado' demasiados errores... Vamos a ver algunos ejemplos:

    Ejemplo de depuración clásica sin el código del runtime:

    Hemos creado una simple aplicación de consola, que realiza una llamada a una función 'foo'. En esta función hemos activado un punto de interrupción, de modo que al ejecutar nos detendremos y veremos como el editor de VS no muestra detalles de estas llamadas en la pila de llamadas, simplemente lo muestra cómo "[External Code"].

    deb1

    Desactivar "My Just Code"

    El siguiente paso consiste en desactivar esta opción en las opciones de depuración, para así poder acceder a los detalles que no forman parte de nuestro código.

    deb2 

    Después de desactivar esta opción, observaremos que eran llamadas realizadas antes de alcanzar a la función destino. Como el compilador no conoce en tiempo de compilación que función debe ser llamada, delega la llamada al enlazador del runtime. Lo que observamos en la pila de llamadas en medio del código de nuestras funciones es el código de la librería del runtime de VB, y si intentamos acceder a él haciendo doble click nos mostrará un mensaje diciendo que no existe código disponible para la localización actual:

    deb3

    Configurar la información de origen del código

    Ahora, desde que el código de Microsoft.VisualBasic ha sido liberado nosotros podemos decirle a Visual Studio dónde encontrarlo. Para ello, usaremos el soporte de servidor de código en VS (para que esto funcione deberá estar instalado el siguiente hotfix: Visual Studio 2008 QFE, que actualiza el componente del depurador que trae los ficheros de código:

    1. En Visual Studio 2008 seleccionar Tools -> Options -> Debugging
    2. En la pestaña “General”
                 a. Desmarcar “Just My Code”
                 b. Marcar “Enable Enable Source Server Support”

    deb4

    En este paso vamos a decirle a VS dónde encontrar los símbolos, así como dónde vamos a guardarlos en local. Este paso es importante porque acceder a los símbolos en una ubicación local es mucho más rápido que acceder desde un servidor remoto.

    En la pestaña “Symbols”

    a. Hacer click en el icono "nuevo" y agregar la ubicación remota: http://referencesource.microsoft.com/symbols
    b. Marcar la casilla “Search the above locations only when symbols are loaded manually”
    c. Introducir una ruta de acceso local en el campo de texto. Debe ser una ubicación sobre la cual tengamos permiso de escritura.
    d. Pulsar OK

    deb5

    A continuación, haremos clic con el botón derecho sobre el primer marco del runtime y seleccionaremos "LoadSymbols". Esperaremos a que los símbolos se carguen (esto se demorará un momento la primera vez, y nos preguntará si aceptamos el EULA). Una vez cargados los símbolos, podemos hacer doble clic y ¡qué diferencia! ¡Ahora podemos ver el código fuente del runtime! (En un primer momento tal vez se tome demasiado tiempo, pero una vez tenga los símbolos en la caché local no volverá a suceder...)

    deb6

    Estableciendo puntos de interrupción dentro del runtime de VB

    Y todavía más. Ahora nosotros podemos navegar con F11 a través del código del runtime e incluso establecer puntos de interrupción. Si eres un poco curioso puedes establecer un punto de interrupción al principio de la llamada y reiniciar el proyecto. Una vez alcances el punto de interrupción podrás navegar con F11 a través del código y ver cómo fue implementado.

    Notaremos que hay una advertencia - Es debido que el código del servidor no es "exactamente" el mismo. Existen unas pequeñas diferencias, como los flags de copyright al final, etc. Debido a eso VS se queja de la diferencia del código.

    deb7

    Esto puede solucionarse fácilmente haciendo clic con el botón derecho sobre el punto de ruptura, abriendo el menú "Location" y seleccionando "Allow source file to be different from the original location". Existe un modo de hacerlo de forma global, pero que no recomendamos porque afectaría a todos los proyectos.

    También para estar seguros de que los símbolos son cargados antes de que la ejecución alcance al punto de ruptura, necesitamos deshabilitar la opción "manual only" de la carga de los símbolos.

    deb8

    Esto causará que todos los símbolos serán cargados bajo demanda y esto incluye algunas librerías del Framework .NET, con lo que esto puede tomar bastante tiempo cuando lanzamos la sesión de depuración. Una vez los símbolos han sido cargados deberíamos ser capaces de alcanzar este punto de ruptura en el runtime.

    deb9

    ¿Que hay dentro del MsgBox?

    Otro escenario interesante que debería funcionar ahora es la navegación mediante F11 por el interior de las funciones del runtime de VB. Una de las más usadas por todos parece ser el MsgBox, de modo que vamos a dar una ojeada a su interior. En un nuevo proyecto podemos usar una llamada a MsgBox e insertar un punto de interrupción:

    deb10

    Cuando alcancemos el punto de interrupción, basta con pulsar F11 y Wow!!! Ahora podemos introducirnos dentro del código de la función MsgBox, navegar a través de él y ver como funciona.

    deb11

    Questiones conocidas, FAQ:

    1) Acceder a los símbolos de forma remota no es demasiado rápido. Esto causa ciertos retrasos en VS cuando los recursos son accedidos. Incluso con la caché local activada en algunos escenarios VS comprueba cuándo la información es actual. Podemos sortear esto yendo a: Tools -> Options -> Debugging -> Symbols configuration, y desmarcando “Search the above locations when symbols are loaded manually”. Y posteriormente depurar el proyecto. Tardará un tiempo en cargar todos los símbolos (incluidas varias librerías .NET) en la configuración (en algunas ocasiones pueden llegar a cargarse más de 50Mb). Después de que VS termine la carga, volver a Tools -> Options -> Debugging -> Symbols y marcar “Search the above locations…”. Esto hará que el depurador no se conecte al servidor cuando nosotros no queremos.

    2) En ocasiones podemos observar que algunas variables o métodos no están disponibles. El motivo es porque estamos usando una versión optimizada. Un resultado de usar versiones optimizadas es que cierta información no está disponible. De modo que tenemos el código pero no somos capaces de acceder a los datos o agregar puntos de ruptura. Por otra parte, todo lo demás debería ser normal.

    3) Funcionará con la edición VB Express? No. Esta funcionalidad no está disponible para esta edición ya que no incorpora estos componentes de Visual Studio.

     

    Es posible consultar el artículo original aquí.

    Saludos desde Andorra,

    ** crossposting desde el blog de Lluís Franco en geeks.ms **
    Enviado Jan 22 2008, 12:12 PM por lfranco con no comments
    Archivado en: ,,
  • Mejoras en el IDE de VB2008

    :-)

    Siempre me he sentido más cómodo escribiendo código C# que VB dentro del IDE de Visual Studio 2005, tal vez será porque el soporte para IntelliSense está más desarrollado, porque me conozco mejor los atajos de teclado, o quizás porque debido a la sintaxis del lenguaje escribo menos para realizar lo mismo (aquí he tenido alguna discusión en la terraza de un bar con el Guille :-P).

    intellisense

    La cuestión es que ahora el equipo de VB2008 acaba de publicar un post en el que se explican las *muchas* mejoras que se han realizado en cuanto al soporte para IntelliSense. No las voy a enumerar aquí pero se resumen en el título del post: IntelliSense Everywhere!

    Tal vez ahora le pague aquella cerveza que nos apostamos al Guille... ;-)

    ** crossposting desde el blog de Lluís Franco en geeks.ms **
  • Buscas un traductor friki?

    Nunca dejan de sorprenderme las frikadas que uno encuentra en la Web. No se trata de una camiseta con un equalizador que muestra el sonido de ambiente, y tampoco de un lanzamisiles USB, naaaa... En esta ocasión os quiero mostrar algo con lo que todo friki de Star wars ha soñado desde que era pequeñito: Nada más y nada menos que un traductor humano -> R2D2!!!

    R2D2

    Puedes especificar el texto a traducir y hasta descargarlo para ponerlo como melodía para tu móvil (aunque esto se reserva para los más frikis).

    Darle un vistazo, vale la pena: http://r2d2translator.com/

    ** crossposting desde el blog de Lluís Franco en geeks.ms **
  • TableAdapters transaccionales? Si por favor!

    ¿Usar DataSets Tipados o no? Uhm...

    Hola a todos,

    En el proyecto que actualmente me ocupa, al desarrollar la capa de acceso a datos he pensado usar DataSets Tipados en lugar de otro modelo habitual basado en clase - colección genérica. ¿Por qué? Bueno porque en principio simplifica el desarrollo, ya que (cómo ya sabéis) el diseñador de DataSets se ocupa de generar las operaciones básicas de lectura/escritura en la base de datos. Esto ahorra bastante tiempo de desarrollo y permite centrarnos en lo realmente importante: La lógica de la aplicación.

    Además este método es de fácil extensión mediante el uso de las clases parciales (una de las características que más me gustan del FWK 2.0), de forma que si deseamos agregar más funcionalidad al adaptador, podemos hacerlo extendiendo la clase de forma muy sencilla:

     
    Namespace AdventureWorksDALTableAdapters
        Partial Public Class CurrencyTableAdapter
            'Implementar aquí el código que extiende la clase
        End Class
    End Namespace

    ¿Fácil verdad? Por un lado tenemos una herramienta que nos genera automáticamente el código de la capa de datos, y que además nos permite extenderlo… entonces ¿dónde está el truco?

    Bueno, los DataSets Tipados aportan un montón de ventajas pero también tienen sus inconvenientes: Por ejemplo, nunca serán tan ligeros como una lista genérica de objetos, ya que precisamente aportan mucha funcionalidad (que no siempre es necesaria). Por otro lado el hecho de ser un código autogenerado hace que mucha gente (yo al principio) lo mire con cierto recelo, ya que al margen de los patrones habituales cada desarrollador tiene su propio método de hacer las cosas, y muchas veces uno piensa que sólo su código es el apropiado (ese defecto lo tenemos muchos :-P).

    AdventureWorksDAL

    Figura 1 - DataSets Tipados generados a partir de la B.D. AdventureWorks

    Pero tal vez el mayor inconveniente que particularmente les encontraba a los DataSets Tipados es lo complicado que resulta utilizarlos en un entorno transaccional, más cuando esto es algo básico, que casi todo desarrollador necesita utilizar hoy en día.

    El contexto transaccional ¿cómo definirlo?

    ¿Quién no se ha encontrado ante la necesidad de realizar varias operaciones de lectura/escritura contra un origen de datos que precisen que se validen todas como una sola? El clásico ejemplo sencillo es el de un documento que contiene n líneas, aunque podemos imaginarnos lo que sería de los entornos bancarios sin la posibilidad de realizar transacciones… Pero sigamos, no quiero apartarme de tema que nos ocupa.

    Actualmente el Framework proporciona dos alternativas para el uso de entornos transaccionales, la primera consiste en el viejo modelo conocido por todos, el clásico, en el que a partir de una conexión abierta con un origen de datos se inicia una transacción, a continuación se realizan todas las operaciones implicadas, y posteriormente se termina con éxito (Commit) o con error (Rollback), de forma que todas las operaciones se guardan o se vuelve al estado inicial.

    SqlTransaction

    Figura 2 - La clase SqlTransaction

    La segunda alternativa es nueva de la versión 2.0 del Framework y consiste en el uso del nuevo espacio de nombres “System.Transactions” que forma parte de los "Enterprise Services Team" de Microsoft. La verdad es que el concepto es muy atractivo ya que permite cosas impensables hasta ahora, como por ejemplo definir un ámbito de contexto transaccional mediante una cláusula Using - End using, en la que todas las operaciones realizadas pertenecerán implícitamente a dicho contexto transaccional.

    TransactionScope

    Figura 3- La clase TransactionScope

    Otra característica destacable de este nuevo modelo es que permite escalar de forma automática una transacción, pasando de transacción ligera a transacción distribuida. Esto es así porque con al definir una nueva transacción, por defecto se intenta crear una transacción ligera, que significa que sólo accede a un recurso y es manejada por el “Lightweight Transaction Manager” o LTM, incluido con el espacio de nombres "System.Transactions". En el momento que esa transacción excede las capacidades del LTM, ya sea porque accede a un segundo recurso en la misma transacción o porque utiliza múltiples dominios de aplicación, ésta es promovida a lo que se llama "transacción distribuida" y pasa a ser manejada por el servicio coordinador de transacciones distribuidas o DTC.

    Este proceso se realiza de forma automática, y la transacción resultante (que es controlada por COM+) ofrece un rendimiento bastante inferior al anterior, con que lo convierte en menos atractivo a nuestros ojos salvo en algunos casos puntuales (como implementar transaccionalidad entre distintas bases de datos).

    DTC

    Figura 4 - El coordinador de transacciones distribuidas en acción

    A día de hoy, utilizar esta nueva metodología transaccional tiene una serie de desventajas frente al modelo clásico, ya que lo interesante sería usar siempre que fuese posible transacciones ligeras, y esto no siempre es posible. De hecho es bastante complicado ya que actualmente sólo existe soporte para SQL Server 2005, así que si usamos una versión anterior u otro motor de datos como Oracle no podremos usar las transacciones ligeras. Del mismo modo, antes he comentado que si se exceden las capacidades del LTM también se promocionan automáticamente a transacciones distribuidas, y realmente es bastante sencillo que esto ocurra. Basta con utilizar dos variables de conexión (aunque realmente apunten a la misma) dentro del contexto transaccional para promoverse a transacción distribuida.

    A lo que íbamos, creando un adaptador transaccional

    Bien, armado con el conocimiento de lo que nos ofrece el Framework me propuse implementar transaccionalidad en mis DataSets Tipados, algo sencillo en su concepto pero complicadillo en su implementación ya que en el código que se genera automáticamente cada adaptador dispone de una colección de objetos Command, y debemos alterar su comportamiento predeterminado para lograr nuestro propósito.

    ¡Manos a la obra! Empezaremos por definir el modelo transaccional, usaremos transaccionalidad clásica mediante el uso del objeto SqlTransaction. Partiremos de un modelo de datos conocido por todos: la base de datos AdventureWorks, y en esta ocasión usaremos el viejo y bueno VB como lenguaje de desarrollo.

    Una vez tenemos en nuestro proyecto un conjunto de datos llamado AdventureWorksDAL.xsd (similar al de la figura 1) con los DataAdapters y DataTables necesarios, vamos extender las clases generadas mediante el uso de clases parciales. Para ello es importante saber que las clases a extender se encuentran dentro de un namespace llamado igual que el archivo del conjunto de datos más el sufijo “TableAdapters”:

    Imports System.Data.SqlClient
     
    Namespace AdventureWorksDALTableAdapters
        Partial Public Class CurrencyTableAdapter
     
            Private _Transaction As SqlTransaction
     
            Public Property Transaction() As SqlTransaction
                Get
                    Return _adapter.SelectCommand.Transaction
                End Get
                Set(ByVal value As SqlTransaction)
                    If _adapter Is Nothing Then InitAdapter()
                    For Each cmd As SqlCommand In Me.CommandCollection
                        cmd.Transaction = value
                    Next
                    If Not _adapter.InsertCommand Is Nothing Then _
                        _adapter.InsertCommand.Transaction = value
                    If Not _adapter.UpdateCommand Is Nothing Then _
                        _adapter.UpdateCommand.Transaction = value
                    If Not _adapter.DeleteCommand Is Nothing Then _
                        _adapter.DeleteCommand.Transaction = value
                End Set
            End Property
     
        End Class
    End Namespace 

    Nótese que el código crea una propiedad de tipo SqlTransaction, a la cual asignaremos posteriormente el valor de una transacción iniciada, y que dicho valor será propagado por todos los objetos SqlCommand que contiene el adaptador llamado “_adapter”, el cual es privado e inaccesible por cualquier otro método que no sea la extensión de esta clase parcial.

    Una vez realizado esto por cada una de las clases de nuestro conjunto de datos, vamos a ver cómo se utilizan estas clases en un entorno real. Let’s play! En primer lugar comprobaremos si la transaccionalidad en un solo adaptador funciona y posteriormente complicaremos un poco más el ejemplo y realizaremos cambios en distintos adaptadores para ver si realmente disponemos de un completo y robusto entorno transaccional.

    Imports System.Data.SqlClient
    Imports TestSqlTransaction_Adapters.AdventureWorksDAL
    Imports TestSqlTransaction_Adapters.AdventureWorksDALTableAdapters
     
    Public Class Form1
     
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim Transaction As SqlTransaction = Nothing
            Try
                Dim CurrencyAdapter As New CurrencyTableAdapter()
                'Abre la conexión, inicia la trasnsacción y la asigna 
                'a la nueva propiedad 'Transaction' para su propagación
                CurrencyAdapter.Connection.Open()
                Transaction = CurrencyAdapter.Connection.BeginTransaction()
                CurrencyAdapter.Transaction = Transaction
                Using Currencies As CurrencyDataTable = CurrencyAdapter.GetData()
                    Dim Currency As CurrencyRow = Currencies.FindByCurrencyCode("EUR")
                    Currency.Name += "*"
                    Dim Changes As CurrencyDataTable = _
                      CType(Currencies.GetChanges(), CurrencyDataTable)
                    If Not Changes Is Nothing Then CurrencyAdapter.Update(Changes)
                End Using
                Dim r As DialogResult = MessageBox.Show( _
                  "Save changes to EUR?", _
                  "Transaction", MessageBoxButtons.YesNo)
                If r = Windows.Forms.DialogResult.Yes Then
                    Transaction.Commit()
                Else
                    Transaction.Rollback()
                End If
            Catch ex As Exception
                Transaction.Rollback()
                MessageBox.Show(ex.Message)
            End Try
     
        End Sub
    End Class

    El ejemplo anterior ha sido sencillo y hemos podido comprobar cómo configurar nuestro adaptador y observar cómo era capaz de revertir a su estado anterior o de guardar los cambios. Vamos a realizar el mismo proceso con varios adaptadores para ver un ejemplo de uso un poco más real, de los de la calle vamos…

    Imports System.Data.SqlClient
    Imports TestSqlTransaction_Adapters.AdventureWorksDAL
    Imports TestSqlTransaction_Adapters.AdventureWorksDALTableAdapters
     
    Public Class Form1
     
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim Transaction As SqlTransaction = Nothing
            Try
                Dim CurrencyAdapter As New CurrencyTableAdapter()
                Dim Currency As CurrencyRow
                'Abre la conexión, inicia la trasnsacción y la asigna 
                'a la nueva propiedad 'Transaction' para su propagación
                CurrencyAdapter.Connection.Open()
                Transaction = CurrencyAdapter.Connection.BeginTransaction()
                CurrencyAdapter.Transaction = Transaction
                Using Currencies As CurrencyDataTable = CurrencyAdapter.GetData()
                    Currency = Currencies.FindByCurrencyCode("EUR")
                    Currency.Name += "*"
                    Dim Changes As CurrencyDataTable = _
                      CType(Currencies.GetChanges(), CurrencyDataTable)
                    If Not Changes Is Nothing Then CurrencyAdapter.Update(Changes)
                End Using
     
                Dim CurrencyRateAdapter As New CurrencyRateTableAdapter()
                'Propaga la conexión y la transacción a este adapter
                CurrencyRateAdapter.Connection = CurrencyAdapter.Connection
                CurrencyRateAdapter.Transaction = Transaction
                Using CurrencyRates As CurrencyRateDataTable = CurrencyRateAdapter.GetData()
                    'Inserta una nueva fila con una cotización a fecha de hoy
                    Dim oNewRate As CurrencyRateRow = _
                      CurrencyRates.AddCurrencyRateRow(DateTime.Today, Currency, _
                        Currency, 10D, 11D, DateTime.Now)
                    Dim Changes As CurrencyRateDataTable = _
                      CType(CurrencyRates.GetChanges(), CurrencyRateDataTable)
                    If Not Changes Is Nothing Then CurrencyRateAdapter.Update(Changes)
                End Using
                Dim r As DialogResult = MessageBox.Show( _
                  "Save changes to EUR?", _
                  "Transaction", MessageBoxButtons.YesNo)
                If r = Windows.Forms.DialogResult.Yes Then
                    Transaction.Commit()
                Else
                    Transaction.Rollback()
                End If
            Catch ex As Exception
                Transaction.Rollback()
                MessageBox.Show(ex.Message)
            End Try
     
        End Sub
    End Class

    Creo que con este ejemplo queda claro que la implementación de transaccionalidad en objetos TableAdapter no es demasiado complejo, y el resultado bien merece la pena. Al fin y al cabo ¿quién no necesita transaccionalidad hoy en día?

    Saludos a todos y ser buenos, o los reyes no os traerán nada...

    ** crossposting desde el blog de Lluís Franco en geeks.ms **
Ofrecido por Community Server (Commercial Edition)