martes, 21 de agosto de 2018 2 comentarios

Error al autenticarse en Escritorio Remoto por Actualización de Windows 10

Recientemente al actualizar Windows 10 (desde mayo 2018) está presentándose un problema de autenticación que no deja establecer una conexión a un servidor Windows remoto mediante la Conexión a Escritorio Remoto de Windows 10 y mostrando el siguiente cuadro de diálogo:


El problema es causado por la falta de una clave de registro que no se ha añadido con la actualización y que a continuación explico como agregarla.

Primero hay que abrir el Editor del Registro de Windows (regedit.exe) con cualquier método conocido por ejemplo, en Windows 10 basta con hacer click en el botón Inicio de Windows y teclear "regedit" seguido de un "Enter" como se puede ver en la siguiente imagen:



O bien, puede pulsar la siguiente combinación de teclas "Windows + R" para que se presente la ventana Ejecutar y ahí tecleamos "RegEdit" seguido de un "Enter" como se ve en la siguiente imagen:



Después hay que ir a la siguiente ruta de registro

"\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" y hacer lo siguiente:


  • Agregar una nueva Clave de Registro con la siguiente descripción "CredSSP"
  • Abrir esa clave y agregar una nueva clave dentro de ella con la siguiente descripción "Parameters"
  • Abrir esta nueva clave y agregar un Valor de DWORD (32 bits) con el nombre "AllowEncryptionOracle" y el valor DECIMAL = 2

Para que finalmente quede como en la siguiente imagen:


Y listo!! Con eso ya se podrá establecer sin problemas una conexión con un servidor Windows remoto mediante la Conexión a Escritorio Remoto de Windows 10.
miércoles, 30 de septiembre de 2015 0 comentarios

Predicates de manera fácil

De acuerdo a la documentación del .Net framework, el delegado genérico Predicate es algo que “representa un método que define un conjunto de criterios y determina si el objeto especificado cumple con esos criterios”. Pero ¿que quiere decir eso?. Leer la documentación que escriben los de Microsoft es fácil, pero no siempre se comprende todo lo que dice. Así que vamos a desmenuzar esa definición antes de mostrar algo de código para ver si se entiende mejor.

La definición dice que un Predicate “representa un método…”, y como los Predicates son un tipo de delegado, debemos entender que el Predicate es más bien un puntero a una función que realiza el trabajo.

Después dice que ese método al que apunta el Predicate “define un conjunto de criterios…” así que, la función contiene en su interior un conjunto de condiciones que se compararán con ciertas propiedades del objeto que se recibe como parámetro en la función.

Y para finalizar el análisis de la definición, ésta dice que “determina si el objeto especificado cumple con esos criterios”, es decir, que una vez que se hagan las comparaciones necesarias en el código interno de la función a la que apunta el Predicate, se debe informar si el objeto que se recibe como parámetro cumple o no con dichas condiciones. Es por eso que si se fijan, me he referido al método en estos párrafos como “función”, ya que se debe devolver un valor booleano que informe si el objeto que se está evaluando cumple o no con los criterios especificados dentro del método.

Pero ¿de qué objeto habla?, ¿que objeto es el que se recibe como parámetro?, ¿que objeto se evalúa?.

Más adelante veremos que cuando se pasa la dirección de la función que es utilizada como Predicate, en ningún momento se pasa algún argumento, entonces empiezan a aparecer las “cochinas” dudas y empieza uno a enredarse todito. Así que vamos aclarando eso del objeto que se recibe como parámetro de una vez.

Los delegados genéricos conocidos como Predicates, hasta donde yo sé, solo se pueden usar como argumentos en métodos de colecciones, listas, arrays, etc., es decir, en métodos de colecciones de objetos como por ejemplo Find, FindAll, FindLast, Exists, etc.. Por lo que es fácil deducir que los Predicates son como “callbacks”, es decir, funciones que se ejecutan y vuelven al punto donde fueron llamadas una y otra vez. No como funciones recursivas, sino que éstas son llamadas como en un ciclo foreach que se ejecuta tantas veces como elementos tenga la colección y que a su vez, al ir recorriendo dicha colección, va  pasando el elemento en turno como argumento a la función a la que apunta el Predicate para que revise si cumple con las condiciones en el interior de dicho método. Este bucle foreach del que hablo es creado cuando nuestro código es compilado y por lo tanto es prácticamente invisible para nosotros a menos que nos pongamos a visualizar el código IL que se genera cuando se compila nuestro programa.

Ahora bien, creo que es suficiente de tanta palabrería, así que veamos algo de código para terminar de entender todo este rollo que acabamos de leer. Así que supongamos que tenemos una clase Empleado definida de la siguiente forma:

  1. Public Class Empleado
  2.     Public Property Nombre As String
  3.     Public Property ApellidoPaterno As String
  4.     Public Property ApellidoMaterno As String
  5.     Public Property Edad As Byte
  6.     Public Property Salario As Decimal
  7.  
  8. End Class

Y que cargamos una lista con objetos de este tipo, por ejemplo:

  1. Sub CargaLista(ByVal lista As List(Of Empleado))
  2.     lista.Add(New Empleado() With {.Nombre = "Noé", .ApellidoPaterno = "García", .ApellidoMaterno = "Urías", .Edad = 30, .Salario = 15000.0})
  3.     lista.Add(New Empleado() With {.Nombre = "Enrique", .ApellidoPaterno = "Chávez", .ApellidoMaterno = "Castillo", .Edad = 42, .Salario = 16000.0})
  4.     lista.Add(New Empleado() With {.Nombre = "José", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López", .Edad = 50, .Salario = 10000.0})
  5.     lista.Add(New Empleado() With {.Nombre = "Julio César", .ApellidoPaterno = "Salazar", .ApellidoMaterno = "Lindoro", .Edad = 45, .Salario = 8000.0})
  6.     lista.Add(New Empleado() With {.Nombre = "Arturo", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López", .Edad = 37, .Salario = 12000.0})
  7. End Sub

Si tuviéramos que hacer búsquedas en esta lista utilizando por ejemplo el método Find de la misma, tendríamos que crear un delegado de tipo Predicate para identificar el elemento que coincide con la condición de búsqueda. He aquí una muestra:

  1. Private Function BuscaPorSalario(ByVal e As Empleado) As Boolean
  2.     If e.Salario = varSalario Then
  3.         Return True
  4.     Else
  5.         Return False
  6.     End If
  7. End Function

Y en el método Find pasar la dirección de la función que será utilizada como Predicate, por ejemplo:

  1. Dim oEmpleado As Empleado = lista.Find(AddressOf BuscaPorSalario)

Al pasar la dirección de memoria al método Find de la lista, el compilador crea un bucle interno en el código IL para utilizar la función de delegado BuscaPorSalario con cada elemento de la lista para ver si la condición de búsqueda se cumple. Cuando el salario de uno de los empleados en la lista coincida con el del valor de la variable varSalario, la función Predicate devolverá Verdadero y el método Find seleccionará ese objeto para ser asignado como resultado a la variable oEmpleado.^

Para finalizar voy a decir que este tipo de delegados es fácil de sustituír por expresiones lambda, ya que con ellas se reduce la cantidad de código y la legibilidad del mismo. Les recomiendo leer esta otra entrada del blog para entender mejor qué son y cómo se usan.

Y eso es todo, espero que en verdad haya sido fácil entender qué son y cómo funcionan los Predicates. Hasta pronto.

lunes, 28 de septiembre de 2015 0 comentarios

Expresiones Lambda de manera fácil

Para los que usan LINQ, las expresiones lambda son algo conocido ya que la mayor parte de los métodos de ese lenguaje usan delegados para permitirnos ordenar, filtrar, proyectar y realizar acciones sobre colecciones de objetos.

Pero… ¿qué es una expresión lambda?. Bueno, pues una Expresión Lambda es una función o subrutina con las siguientes características:

  • No tiene nombre o sea, es anónima
  • Puede contener una sola línea o múltiples líneas
  • No tiene modificadores de acceso (Public, Private, Overloads, etc.)
  • Su tipo de retorno es inferido, y
  • Se puede usar en cualquier lugar donde se permita usar un delegado, por ejemplo, en los métodos Find o FindAll de las listas genéricas.

Las expresiones lambda se incluyeron a partir de la versión 3.0 del .Net Framework como una especie de sustitución de los métodos anónimos que se podían usar con C# desde la versión 2.0 de dicho framework y que nunca estuvieron disponibles para VB y que servían para facilitar la declaración “inline” de un método usando la palabra reservada delegate.

Como las expresiones lambda son una manera más corta y concisa de escribir delegados, se pueden usar en cualquier parte donde los delegados sean admitidos así que, al igual que éstos, también pueden ser pasadas como argumentos a funciones y procedimientos.

Una expresión lambda puede ser asignada a una variable de la misma manera que se puede hacer con un delegado para usarse posteriormente, por ejemplo:

  1. Dim Suma = Function(numero1 As Integer, numero2 As Integer) numero1 + numero2 

En la asignación del ejemplo, se puede ver que la expresión lambda se asigna a una variable, la expresión lambda cumple con todas las características mencionadas al principio de este artículo, como que la función no tiene nombre, ejecuta una sola línea de código, no hay modificadores de acceso y no se especifica el tipo de dato de retorno del resultado.

Cabe mencionar que la expresión se pudo haber escrito en mas líneas si hubiera querido, por ejemplo:

  1. Dim Suma = Function(numero1 As Integer, numero2 As Integer)
  2.                Return numero1 + numero2
  3.            End Function

Solo que hay que colocar un End Function o un End Sub según se trate. En el caso de C# bastaría con encerrar las instrucciones de la expresión lambda entre llaves { } que son los especificadores de bloques de código de ese lenguaje de programación.

Ahora, para usar esa expresión lambda asignada a la variable “Suma”, bastaría hacer algo como lo siguiente para mostrar un número 3 en la consola de salida:

  1. Console.WriteLine(Suma(1, 2))

Pero se pueden preguntar ¿y que ventaja tienen entonces las expresiones lambda? eso también se puede hacer con una función común y corriente sin necesidad de usar una variable y también se puede hacer con un delegado. Pero una de las ventajas de usar expresiones lambda es que permiten simplificar el código y hacerlo todo como decimos en México “de un jalón” de la siguiente manera:

  1. Console.WriteLine((Function(numero1 As Integer, numero2 As Integer) numero1 + numero2)(1, 2))

Incluso, se puede omitir el tipo de datos de los parámetros que recibe la función, quedando como se ve a continuación:

  1. Console.WriteLine((Function(numero1, numero2) numero1 + numero2)(1, 2))

Y si aún así creen que esto no tiene nada de sorprendente ni de útil. Bueno, pues entonces vamos a ver un ejemplo que hará algo más complicado con la intención de esclarecer la utilidad y la conveniencia de las expresiones lambda.

Imaginemos tener una clase Empleado definida de la siguiente forma:

  1. Public Class Empleado
  2.     Public Property Nombre As String
  3.     Public Property ApellidoPaterno As String
  4.     Public Property ApellidoMaterno As String
  5.     Public Property Edad As Byte
  6.     Public Property Salario As Decimal
  7.  
  8. End Class

Y que cargamos una lista con objetos de este tipo, por ejemplo:

  1. Sub CargaLista(ByVal lista As List(Of Empleado))
  2.     lista.Add(New Empleado() With {.Nombre = "Noé", .ApellidoPaterno = "García", .ApellidoMaterno = "Urías", .Edad = 30, .Salario = 15000.0})
  3.     lista.Add(New Empleado() With {.Nombre = "Enrique", .ApellidoPaterno = "Chávez", .ApellidoMaterno = "Castillo", .Edad = 42, .Salario = 16000.0})
  4.     lista.Add(New Empleado() With {.Nombre = "José", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López", .Edad = 50, .Salario = 10000.0})
  5.     lista.Add(New Empleado() With {.Nombre = "Julio César", .ApellidoPaterno = "Salazar", .ApellidoMaterno = "Lindoro", .Edad = 45, .Salario = 8000.0})
  6.     lista.Add(New Empleado() With {.Nombre = "Arturo", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López", .Edad = 37, .Salario = 12000.0})
  7. End Sub

Si tuviéramos que hacer búsquedas en esta lista utilizando por ejemplo el método Find de la misma tendríamos que crear un delegado de tipo Predicate para identificar el elemento que coincide con la condición de búsqueda. He aquí una muestra:

  1. Private Function BuscaPorSalario(ByVal e As Empleado) As Boolean
  2.     If e.Salario = varSalario Then
  3.         Return True
  4.     Else
  5.         Return False
  6.     End If
  7. End Function

Y en el método Find pasar la dirección del Predicate como sigue:

  1. lista.Find(AddressOf BuscaPorSalario)

Al pasar la dirección de memoria al método Find de la lista, el compilador crea un bucle interno en el código IL para utilizar la función de delegado BuscaPorSalario con cada elemento de la lista para ver si la condición de búsqueda se cumple. Pero si quisiéramos buscar por medio de otra propiedad por ejemplo, su apellido paterno o su edad, tendríamos que escribir otro Predicate que identificara el elemento de acuerdo a esa otra propiedad, lo cual es una solución pero no es la ideal. En cambio, usando expresiones lambda, la tarea se simplifica muchísimo. Para demostrarlo, podemos hacer un método de prueba donde se verifica el resultado de algunas búsquedas en la lista, efectuadas utilizando expresiones lambda que usan diferentes propiedades de los elementos como a continuación se muestra:

  1. <TestMethod()>
  2. Sub Prueba()
  3.     Dim lista As List(Of Empleado) = New List(Of Empleado)
  4.     Dim Salario As Decimal = 10000D
  5.  
  6.     CargaLista(lista)
  7.  
  8.     Assert.AreEqual("José", (lista.Find(Function(e) e.Salario = Salario)).Nombre)
  9.     Assert.AreEqual("Noé", (lista.Find(Function(e) e.ApellidoPaterno = "García")).Nombre)
  10.     Assert.AreEqual("Enrique", (lista.Find(Function(e) e.Edad = 42)).Nombre)
  11.  
  12. End Sub 

Como se puede ver en las líneas 8, 9 y 10 del método de prueba, se verifican los resultados de las búsquedas en la lista de objetos empleado pero utilizando expresiones lambda para facilitar la búsqueda de dichos elementos en base a distintas propiedades de los mismos, reduciendo la cantidad de código y facilitando la lectura del mismo, además, si se mira con atención la línea 8, se puede ver como la expresión lambda utiliza la variable Salario declarada e inicializada en la línea 4 para identificar el elemento que cumple con la condición de búsqueda. Esto es posible con las expresiones lambda pero no con los delegados de tipo predicate, ya que las expresiones lambda tienen acceso a cualquier variable local y global visible en el contexto donde se encuentre escrita. En el caso de usar un predicate, la variable Salario tendría que ser visible a nivel de clase, formulario o global.

Ahora para finalizar, demostraré la utilidad de las expresiones lambda retomando el ejemplo de una entrada anterior donde hablaba sobre los delegados (a la que los remito si aún no la han leído) donde se terminó con un procedimiento genérico que implementaba el algoritmo de ordenamiento de burbuja que muestro a continuación:

  1. Public Sub Sort(Of T)(ByVal Lista As IList(Of T), ByVal Comparador As ComparerFunction(Of T))
  2.     Dim HuboCambio As Boolean
  3.  
  4.     Do
  5.         HuboCambio = False
  6.  
  7.         For i As Integer = 0 To Lista.Count - 1
  8.             If Comparador(Lista(i), Lista(i + 1)) > 0 Then
  9.                 Dim oTemp As T = Lista(i)
  10.                 Lista(i) = Lista(i + 1)
  11.                 Lista(i + 1) = oTemp
  12.                 HuboCambio = True
  13.             End If
  14.         Next
  15.  
  16.     Loop While (HuboCambio)
  17.  
  18. End Sub

donde se puede ver que se utiliza un delegado genérico para realizar la comparación entre los elementos de una lista. Por lo que, muestro también la declaración del delegado genérico:

  1. Public Delegate Function ComparerFunction(Of T)(ByVal a As T, ByVal b As T) As Integer

y del método compatible con el delegado genérico:

  1. Private Function ComparePersonByApellidos(ByVal a As Persona, ByVal b As Persona) As Integer
  2.    Return String.Format("{0} {1}", a.ApellidoPaterno, a.ApellidoMaterno).CompareTo(String.Format("{0} {1}", b.ApellidoPaterno, b.ApellidoMaterno))
  3. End Function

Y también un mostraré el método de prueba de estos pequeños trozos de código:

  1. Imports System.Text
  2. Imports DelegatesAndLambdas
  3.  
  4. <TestClass()>
  5. Public Class BubbleSorterTest
  6.  
  7.     <TestMethod()>
  8.     Public Sub CreateAndSortAList()
  9.         Dim lista As List(Of Persona) = New List(Of Persona)
  10.  
  11.         lista.Add(New Persona() With {.Nombre = "Noé", .ApellidoPaterno = "García", .ApellidoMaterno = "Urías"})
  12.         lista.Add(New Persona() With {.Nombre = "Enrique", .ApellidoPaterno = "Chávez", .ApellidoMaterno = "Castillo"})
  13.         lista.Add(New Persona() With {.Nombre = "José", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López"})
  14.         lista.Add(New Persona() With {.Nombre = "Julio César", .ApellidoPaterno = "Salazar", .ApellidoMaterno = "Lindoro"})
  15.         lista.Add(New Persona() With {.Nombre = "Arturo", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López"})
  16.  
  17.         Dim callback As ComparerFunction(Of Persona) = New ComparerFunction(Of Persona)(AddressOf CompareByApellidos)
  18.  
  19.         Call New BubbleSorter().Sort(lista, callback)
  20.  
  21.         Assert.AreEqual("García", lista(3).ApellidoPaterno)
  22.         Assert.AreEqual("Lindoro", lista(4).ApellidoMaterno)
  23.         Assert.AreEqual("Enrique", lista(0).Nombre)
  24.  
  25.     End Sub
  26.  
  27.     Public Function CompareByApellidos(ByVal a As Persona, ByVal b As Persona) As Integer
  28.         Return String.Format("{0} {1}", a.ApellidoPaterno, a.ApellidoMaterno).CompareTo(String.Format("{0} {1}", b.ApellidoPaterno, b.ApellidoMaterno))
  29.     End Function
  30.  
  31. End Class

Ahora, mostraré como se puede simplificar la escritura de este método con la utilización de Expresiones lambda para que vean que no solo sirven para hacer búsquedas.

Si vemos con atención el método de prueba en el bloque de código anterior, cuando el delegado es creado se le pasa la dirección de memoria de la función compatible que es CompareByApellidos la cual recibe un par de parámetros de tipo Persona.  Todo esto está bien, pero para que escribir tanto si se puede hacer de la siguiente manera:

  1. <TestClass()> _
  2. Public Class BubbleSorterWithLambdaExpresionTest
  3.  
  4.     <TestMethod()>
  5.     Public Sub CreateAndSortAList()
  6.         Dim lista As List(Of Persona) = New List(Of Persona)
  7.  
  8.         lista.Add(New Persona() With {.Nombre = "Noé", .ApellidoPaterno = "García", .ApellidoMaterno = "Urías"})
  9.         lista.Add(New Persona() With {.Nombre = "Enrique", .ApellidoPaterno = "Chávez", .ApellidoMaterno = "Castillo"})
  10.         lista.Add(New Persona() With {.Nombre = "José", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López"})
  11.         lista.Add(New Persona() With {.Nombre = "Julio César", .ApellidoPaterno = "Salazar", .ApellidoMaterno = "Lindoro"})
  12.         lista.Add(New Persona() With {.Nombre = "Arturo", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López"})
  13.  
  14.         Call New BubbleSorter().Sort(lista, _
  15.         Function(a, b) String.Format("{0} {1}", a.ApellidoPaterno, a.ApellidoMaterno).CompareTo(String.Format("{0} {1}", b.ApellidoPaterno, b.ApellidoMaterno)))
  16.  
  17.         Assert.AreEqual("García", lista(3).ApellidoPaterno)
  18.         Assert.AreEqual("Lindoro", lista(4).ApellidoMaterno)
  19.         Assert.AreEqual("Enrique", lista(0).Nombre)
  20.  
  21.     End Sub 
  22.  
  23. End Class

Como podemos observar, las líneas de código son menos (un 30% menos aproximadamente), el código es más simple (una vez que te acostumbras a las expresiones lambda) y más legible. Además se resta bastante la complejidad al dejar de utilizar los delegados que funcionan como intermediarios. Aquí todo es más directo. Lo interesante de este fragmento de código ocurre en las líneas 14 y 15 que en realidad son una sola línea pero la dividí por cuestiones de espacio en el blog.

Analizando este par de líneas podemos ver que ya no fue necesario declarar ningún delegado ni la función CompareByApellidos. En su lugar se utilizó una expresión lambda, la cual cumple con todas las características mencionadas al inicio de este artículo al igual que todas las que se usaron en los ejemplos anteriores.

Los parámetros recibidos “a” y “b” son 2 elementos de la lista diferentes pero subsecuentes y el compilador los va remplazando mediante un bucle que crea cuando compilamos el código por lo que nunca lo vemos.

Y bueno… creo que esto es todo. Espero que si llegaron a leer estas últimas líneas consideren que el título de esta entrega fue el correcto.

domingo, 19 de julio de 2015 0 comentarios

Delegados de manera fácil

Hola, en esta entrada del blog intentaré explicar de manera fácil y sencilla que son los delegados y mencionaré algunos casos en que se pueden usar y combinar con Generics para mejorar la reusabilidad de nuestro código.

Para facilitar el entendimiento de estos conceptos partiremos de la implementación de un algoritmo simple y común que cualquier programador debería conocer. Éste es el algoritmo de ordenamiento de Burbuja (Bubble Sort en inglés):

  1. Public Class BubbleSorter
  2.     Public Sub Sort(ByVal Lista As IList(Of Persona))
  3.         Dim HuboCambio As Boolean
  4.  
  5.         Do
  6.             HuboCambio = False
  7.  
  8.             For i As Integer = 0 To Lista.Count - 1
  9.                 If Compare(Lista(i), Lista(i + 1)) > 0 Then
  10.                     Dim oTemp As Persona = Lista(i)
  11.                     Lista(i) = Lista(i + 1)
  12.                     Lista(i + 1) = oTemp
  13.                     HuboCambio = True
  14.                 End If
  15.             Next
  16.  
  17.         Loop While (HuboCambio)
  18.  
  19.     End Sub
  20.  
  21.     Private Function Compare(ByVal a As Persona, ByVal b As Persona) As Integer
  22.         Return a.Nombre.CompareTo(b.Nombre)
  23.     End Function
  24.  
  25. End Class
  26.  
  27. Public Class Persona
  28.  
  29.     Public Property Nombre As String
  30.     Public Property ApellidoPaterno As String
  31.     Public Property ApellidoMaterno As String
  32.  
  33. End Class

Este algoritmo es muy sencillo. Simplemente recorre una lista de objetos de tipo Persona y compara la propiedad Nombre de los objetos que están consecutivos y los intercambia si es necesario para que queden ordenados alfabéticamente de menor a mayor.

Esto está bien pero ¿qué pasa si queremos ordenar la lista de objetos en base a otra propiedad? o ¿qué pasa si los objetos que queremos ordenar son de otro tipo? Sería magnífico si el método Sort de nuestra clase BubbleSort fuera más reusable y más genérico sin necesidad de cambios ¿verdad?

Bueno, pues para eso, podemos extender la funcionalidad de nuestro método de ordenamiento implementando la interface IComparable(Of T) en la clases que la usen, por ejemplo, veamos como quedaría dicha implementación en la clase Persona:

  1. Public Class Persona
  2.     Implements IComparable(Of Persona)
  3.  
  4.     Public Property Nombre As String
  5.     Public Property ApellidoPaterno As String
  6.     Public Property ApellidoMaterno As String
  7.  
  8.     Public Function CompareTo(ByVal otro As Persona) As Integer _
  9.         Implements IComparable(Of Persona).CompareTo
  10.  
  11.         Return Nombre.CompareTo(otro.Nombre)
  12.     End Function
  13. End Class 

Y el método Sort requeriría un par de pequeños cambios:

  1. Public Sub Sort(Of T As IComparable(Of T))(ByVal Lista As IList(Of T))
  2.     Dim HuboCambio As Boolean
  3.  
  4.     Do
  5.         HuboCambio = False
  6.  
  7.         For i As Integer = 0 To Lista.Count - 1
  8.             If Lista(i).CompareTo(Lista(i + 1)) > 0 Then
  9.                 Dim oTemp As T = Lista(i)
  10.                 Lista(i) = Lista(i + 1)
  11.                 Lista(i + 1) = oTemp
  12.                 HuboCambio = True
  13.             End If
  14.         Next
  15.  
  16.     Loop While (HuboCambio)
  17.  
  18. End Sub

Ahora, nuestro método Sort es genérico y acepta una lista de objetos de cualquier tipo que implemente la interface IComparable(Of T). Así el método de ordenamiento del ejemplo no solo puede ser usado con la clase Persona, sino con cualquiera con la única condición de que implemente la interface ya mencionada.

Ahora bien, si tenemos una clase que no podemos modificar porque solo tenemos una librería (DLL) y no su código, de tal forma que no podemos implementar la interface IComparable. Bueno, sería genial si pudiéramos definir el método Compare fuera de la clase BubbleSort o de las clases en la librería de tal forma que este método haga las comparaciones necesarias y se pueda pasar directamente un puntero a dicho método que creamos, hacia el método Sort de nuestra clase BubbleSort.

Bueno, pues eso es posible gracias a una estructura de programación llamada Delegado. Ésta nos permite invocar uno o varios métodos a la vez y se puede ver como un objeto que “apunta” a métodos sin importar si son métodos compartidos de una clase o métodos de una instancia y que se puede usar como punteros seguros a funciones.

Para que se entienda mejor lo que dice el párrafo anterior. Se puede decir que cuando se crea un delegado, éste “guarda” la dirección de memoria (como una referencia) de una función que se pasa como argumento al constructor de dicho delegado. Por ejemplo, cuando se crea o usa un evento, internamente se está usando un objeto delegado, así que cuando este evento es lanzado, el framework examina el delegado relacionado con ese evento y usa la función a la que apunta el delegado.

Ahora bien, volviendo al ejemplo, supongamos que ahora queremos ordenar la lista por la combinación de los apellidos de los objetos Persona. Supongamos también que la clase Persona no es modificable, porque solo tenemos acceso a ella porque es expuesta por una librería de clases (DLL). El método comparador podría quedar de la siguiente manera en un módulo u otra clase en nuestro proyecto:

  1. Private Function ComparePersonByApellidos(ByVal a As Persona, ByVal b As Persona) As Integer
  2.    Return String.Format("{0} {1}", a.ApellidoPaterno, a.ApellidoMaterno).CompareTo(String.Format("{0} {1}", b.ApellidoPaterno, b.ApellidoMaterno))
  3. End Function

Y para poder utilizarlo, podríamos declarar un Delegado compatible con la declaración de este método:

  1. Public Delegate Function ComparerFunction(Of persona)(ByVal a As Persona, ByVal b As Persona) As Integer

O mejor aún, declarar este delegado como genérico y que sea más reusable y útil:

  1. Public Delegate Function ComparerFunction(Of T)(ByVal a As T, ByVal b As T) As Integer

Y para terminar, debemos cambiar el método Sort para que use el delegado y permanezca genérica.

  1. Public Sub Sort(Of T)(ByVal Lista As IList(Of T), ByVal Comparador As ComparerFunction(Of T))
  2.     Dim HuboCambio As Boolean
  3.  
  4.     Do
  5.         HuboCambio = False
  6.  
  7.         For i As Integer = 0 To Lista.Count - 1
  8.             If Comparador(Lista(i), Lista(i + 1)) > 0 Then
  9.                 Dim oTemp As T = Lista(i)
  10.                 Lista(i) = Lista(i + 1)
  11.                 Lista(i + 1) = oTemp
  12.                 HuboCambio = True
  13.             End If
  14.         Next
  15.  
  16.     Loop While (HuboCambio)
  17.  
  18. End Sub

Con estos cambios al método Sort, éste queda como genérico y perfecto para utilizar un delegado que realice las comparaciones que mejor se ajusten a nuestras necesidades aunque no tengamos acceso al código de las clases cuyas instancias se están ordenando.

Para usar este código se puede tomar el siguiente ejemplo:

  1. Imports System.Text
  2. Imports DelegatesAndLambdas
  3.  
  4. <TestClass()>
  5. Public Class BubbleSorterTest
  6.  
  7.     <TestMethod()>
  8.     Public Sub CreateAndSortAList()
  9.         Dim lista As List(Of Persona) = New List(Of Persona)
  10.  
  11.         lista.Add(New Persona() With {.Nombre = "Noé", .ApellidoPaterno = "García", .ApellidoMaterno = "Urías"})
  12.         lista.Add(New Persona() With {.Nombre = "Enrique", .ApellidoPaterno = "Chávez", .ApellidoMaterno = "Castillo"})
  13.         lista.Add(New Persona() With {.Nombre = "José", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López"})
  14.         lista.Add(New Persona() With {.Nombre = "Julio César", .ApellidoPaterno = "Salazar", .ApellidoMaterno = "Lindoro"})
  15.         lista.Add(New Persona() With {.Nombre = "Arturo", .ApellidoPaterno = "Escobedo", .ApellidoMaterno = "López"})
  16.  
  17.         Dim callback As ComparerFunction(Of Persona) = New ComparerFunction(Of Persona)(AddressOf CompareByApellidos)
  18.  
  19.         Call New BubbleSorter().Sort(lista, callback)
  20.  
  21.         Assert.AreEqual("García", lista(3).ApellidoPaterno)
  22.         Assert.AreEqual("Lindoro", lista(4).ApellidoMaterno)
  23.         Assert.AreEqual("Enrique", lista(0).Nombre)
  24.  
  25.     End Sub
  26.  
  27.     Public Function CompareByApellidos(ByVal a As Persona, ByVal b As Persona) As Integer
  28.         Return String.Format("{0} {1}", a.ApellidoPaterno, a.ApellidoMaterno).CompareTo(String.Format("{0} {1}", b.ApellidoPaterno, b.ApellidoMaterno))
  29.     End Function
  30.  
  31. End Class

En este método de prueba, se puede ver como se agregan 5 objetos de tipo Persona a una Lista, luego se crea una instancia del delegado para realizar las comparaciones, posteriormente se ejecuta la ordenación y al final se evalúa el ordenamiento y se confirman los resultados.

Este tema da para más e incluso, puedo decir que el código donde se manejan delegados se puede reducir aún más con el uso de Métodos Anónimos, pero eso solo se puede hacer con C# ya que nunca ha estado disponible para VB y creo que nunca lo estará gracias a la aparición de las fabulosas Expresiones Lambda, que son una forma corta y concisa de escribir delegados, pero hablaré más sobre éstas en otra entrada del blog.

Espero haber sido lo suficientemente claro y explícito para que este tema se entendiera ya que, cuando hable posteriormente sobre las Expresiones Lambda  los ejemplos estarán relacionados con los que aquí se han mostrado y porque cuando hable de como usar los distintos tipos de delegados y como usar todo esto con Cooperator Framework, un buen entendimiento de estas estructuras de programación será necesario.

domingo, 21 de septiembre de 2014 0 comentarios

Forzando el Lazy Load de Cooperator Framework

El patrón llamado “Lazy Load”  o “Carga diferida” (porque traducido literalmente al español sería “carga perezosa” y no me gusta como se escucha) es una técnica usada para retrasar la carga de un objeto anidado en otro hasta el momento en que éste es necesario, es decir – no se moleste señor, si lo necesito, sé como comunicarme con usted, así que yo lo llamo después – y es un gran placer saber y comunicar que los objetos/entidades creados mediante Cooperator Framework cuentan con la implementación de este patrón de diseño.

Este patrón se puede implementar de varias formas y Cooperator Framework utiliza una de ellas llamada “Lazy Initialization” combinada con otros patrones de diseño como “factory”, “factory method pattern” y “multiton pattern”. Este patrón de inicialización diferida (lazy initialization)  se basa comúnmente en el uso de una bandera que indica si el objeto anidado ha sido cargado con anterioridad y Cooperator simula esta bandera verificando si el objeto a cargar es Nulo o no.

Como todo en la vida, usar algo con responsabilidad y cautela puede ayudar mucho y abusar de ese algo puede causar grandes problemas y todo eso se explica en los videos que tratan el tema de la impedancia (4 y 5) así como en el documento de referencia del Framework y el Modeler, donde se ve como activar esta característica del lazy load en los objetos/entidades y también en las listas anidadas mediante el Modeler, por lo que en esta entrada del blog me centraré en el uso de esta implementación para resolver un problema que para mí digamos, es poco común.

Antes de mostrar el código, he de decir que recientemente encontré una forma al parecer más correcta de solucionar el problema y evitar el uso de la carga diferida para resolverlo, pero de todos modos voy a continuar con este tema porque tal vez a alguien le sirva de ayuda en algún momento, así que primeramente voy a hacer una breve descripción del problema para que se entienda mejor el código y el porqué tuve que resolverlo utilizando lazy load y antes de finalizar la entrada mostraré el código con la solución que definitivamente creo más correcta y conveniente.

Resulta que hace un tiempo se me presentó un pequeño problema que, aunque sé como resolverlo, no me había tocado hacerlo usando Cooperator Framework y tenía que ver con la longitud de algo que en México se hace llamar “Registro Federal de Contribuyentes” o más conocido por sus siglas como RFC. El RFC es un código de identificación tributaria equivalente al CUIT en Argentina, al NIF en España, al RUC de Perú y Paraguay, al RUT  de Uruguay, al NIT que se usa en varios países de centroamérica o al SSN en Estados Unidos. Y pues bien, este dichoso RFC puede tener una longitud no menor de 12 caracteres y no mayor de 13 y esta variación de longitud se debe a otra cosa llamada “Clasificación Fiscal” (según el SAT o Servicio de Administración Tributaria). Esta clasificación fiscal es la manera en que el SAT distingue a los contribuyentes que son “Personas Físicas de otro tipo de contribuyentes a los que llaman “Personas Morales”, de tal modo que, los RFC de las personas físicas tienen una longitud de 13 caracteres y los RFC de las personas morales tienen una longitud de 12 caracteres.

Pues bien,  tengo en una aplicación una entidad Proveedor la cual puede o no estar almacenada en la base de datos con un RFC, ya que muchas veces por hacer el proceso más rápido, se puede omitir su RFC al darlo de alta en el sistema y agregarlo posteriormente pero, de tener RFC, éste debe cumplir con el requisito de la longitud. La aplicación no valida la formación del RFC, solo su longitud y por tal motivo, la validación de esta característica en la entidad Proveedor se hace utilizando su clasificación fiscal.

Para solucionar el detalle de la longitud del RFC, se modeló la entidad Proveedor con un objeto anidado llamado ClasificacionFiscalEntity que por default se carga con este modo “lazy load” al igual que todos los objetos / entidades (xxxEntity) anidados que son agregados por asociación (véase el documento de Referencia del Framework páginas 15,16 y 18) pero primero, se crean mediante el constructor de la clase principal (en este caso Proveedor) de la siguiente forma:

  1. Public Partial Class Proveedor
  2.     Inherits Objects.ProveedorObject
  3.     Implements IMappeableProveedor
  4.     Implements IEquatable(Of Proveedor)
  5.     Implements ICloneable
  6.  
  7.     #Region "Ctor"
  8.  
  9.     Public Sub New()
  10.         MyBase.New()
  11.         If _ClasificacionFiscalEntity Is Nothing Then _ClasificacionFiscalEntity = New Objects.ClasificacionFiscalObject()
  12.         If _CondicionDePagoEntity Is Nothing Then _CondicionDePagoEntity = New Objects.CondicionDePagoObject()
  13.         If _DatosDeContactoEntity Is Nothing Then _DatosDeContactoEntity = New Objects.DatosDeContactoObject()
  14.         If _DomicilioEntity Is Nothing Then _DomicilioEntity = New Objects.DomicilioObject()
  15.         If _MonedaEntity Is Nothing Then _MonedaEntity = New Objects.MonedaObject()
  16.         If _TipoDeAutorizacionEntity Is Nothing Then _TipoDeAutorizacionEntity = New Objects.TipoDeAutorizacionObject()
  17.         If _TipoDeOperacionEntity Is Nothing Then _TipoDeOperacionEntity = New Objects.TipoDeOperacionObject()
  18.         If _TipoDeProveedorEntity Is Nothing Then _TipoDeProveedorEntity = New Objects.TipoDeProveedorObject()
  19.  
  20.     End Sub

En el fragmento de código anterior se puede ver que al crear la nueva entidad, se revisa si los objetos/entidades anidados han sido instanciados y en caso de no ser así, se crean mediante su constructor.

Ahora bien, todo esto funciona bien normalmente, pero en mi caso existe el problema de que la propiedad EsPersonaMoral de ClasificacionFiscalEntity es de tipo Boolean y por lo tanto se inicializa en Falso y es utilizando el valor de esta propiedad que se hace la validación. Por lo que si tengo un código como el siguiente:

  1. If (Me.RFC.Trim().Length <> 12 And Me.ClasificacionFiscalEntity.EsPersonaMoral) Then
  2.    AddError(ProveedorColumn.RFC.ToString("G"), String.Format(msgWrongLenght, "El R.F.C.", 12))
  3. Else
  4.    RemoveError(ProveedorColumn.RFC.ToString("G"), String.Format(msgWrongLenght, "El R.F.C.", 12))
  5. End If
  6.  
  7. If (Me.RFC.Trim().Length <> 13 And (Not Me.ClasificacionFiscalEntity.EsPersonaMoral)) Then
  8.     AddError(ProveedorColumn.RFC.ToString("G"), String.Format(msgWrongLenght, "El R.F.C.", 13))
  9. Else
  10.     RemoveError(ProveedorColumn.RFC.ToString("G"), String.Format(msgWrongLenght, "El R.F.C.", 13))
  11. End If

nunca validará correctamente la longitud de los RFC’s de las personas morales, porque la propiedad EsPersonaMoral siempre tendrá el valor de Falso.

Entonces, para resolver el problema de inicialización de esta propiedad se hace algo que yo llamo “EggedLoad” (o sea “Carga de a Huevo”); bueno no, ya en serio le llamo “Forced LazyLoad” ya que literalmente estoy forzando a que se cargue la entidad ClasificacionFiscal utilizando los métodos compartidos del mecanismo de LazyLoad. Y esto se muestra en el siguiente fragmento de código:

  1. ' Esto forza a que se recargue la propiedad ClasificacionFiscalEntity vía LazyLoad.
  2. ' El campo _ClasificacionFiscalEntity está declarado en Proveedor.Auto.vb
  3. Dim lazyProvider As LazyLoad.ILazyProvider = LazyLoad.LazyProviderFactory.Get(GetType(Objects.ClasificacionFiscalObject))
  4. _ClasificacionFiscalEntity = CType(lazyProvider.GetEntity(GetType(Objects.ClasificacionFiscalObject), _
  5.                                                           New Objects.ClasificacionFiscalObject(Me.IdClasificacionFiscal)),  _
  6.                                    Objects.ClasificacionFiscalObject)
  7.  
  8. If (Me.RFC.Trim().Length <> 12 And Me.ClasificacionFiscalEntity.EsPersonaMoral) Then
  9.    . . .

Con este par de líneas de código (divididas en varias para que no queden tan largas) se logra forzar la recarga de una entidad mediante el mecanismo de lazyload. Lo que sigue a esta recarga, son las validaciones que hacen referencia a la propiedad EsPersonaMoral la cual tendrá ahora el valor correcto.

Ahora bien, la desventaja que tiene hacer esta carga forzada, es que cada vez que se ejecute el método Validate del objeto/entidad se hará una consulta a la base de datos, lo cual podría afectar el rendimiento de la aplicación si se utiliza la técnica del databinding ya que, el método Validate se ejecuta prácticamente cada vez que se cambia el foco de entrada de un control a otro y si a eso se agregan los llamados explícitos a dicho método…pues ya sabrán.

La solución que encontré para evadir estas continuas consultas a la base de datos fue implementar el controlador del evento SelectedIndexChanged del combobox al que tengo enlazada la lista de clasificaciones fiscales haciendo lo siguiente:

  1. Private Sub cboClasificacionFiscal_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboClasificacionFiscal.SelectedIndexChanged
  2.     If bolLoaded Then
  3.         oProveedor.ClasificacionFiscalEntity = CType(cboClasificacionFiscal.SelectedItem, ClasificacionFiscalObject)
  4.  
  5.     End If
  6.  
  7. End Sub

En el fragmento de código anterior se puede ver que cada vez que se cambia el elemento del combobox, se asigna directamente el objeto/entidad asociado con este control a la propiedad ClasificacionFiscalEntity de la entidad Proveedor. La bandera “bolLoaded” se utiliza para que el código de este método no se ejecute durante la carga del control, sino solamente hasta que todo el formulario está cargado.

Con esta sencilla implementación se ahorra mucho tiempo comparado con la técnica del “EggLoad”, porque no hay consultas a la base de datos y además la entidad asociada (ClasificacionFiscalEntity) con el objeto principal (oProveedor) siempre tiene el objeto correcto con sus propiedades cargadas también correctamente.

Y eso es todo, en realidad es mucho rollo para mostrar un par de líneas que son las que importan en esta entrada, pero la explicación del problema era necesaria. Espero que algún día esta entrada le ayude a alguien que use Cooperator Framework y necesite forzar la carga de un objeto/entidad con él mecanismo de lazy load.

 
;