Quantcast
Channel: Planeta Código
Viewing all 2715 articles
Browse latest View live

Variable not found: Etiquetado de consultas en Entity Framework 6

$
0
0
Entity FrameworkPues hablábamos hace unos días del extensor TagWith() de Entity Framework Core que, como recordaréis, venía de perlas para incluir un comentario en las consultas SQL enviadas al servidor de datos para poder identificarlas a posteriori:
LINQ
============================================
var query = context.Friends
.OrderByDescending(friend => friend.Age)
.Take(10)
.Select(friend =>
new { FriendName = friend.Name,
friend.Age,
CountryName = friend.Country.Name
})
.TagWith("Get top 10 older friends with country");

Generated SQL
============================================
-- Get top 10 older friends with country

SELECT TOP(@__p_0) [friend].[Name] AS [FriendName], [friend].[Age],
[friend.Country].[Name] AS [CountryName]
FROM [Friends] AS [friend]
LEFT JOIN [Countries] AS [friend.Country]
ON [friend].[CountryId] = [friend.Country].[Id]
ORDER BY [friend].[Age] DESC
Mientras escribía el post, pensaba que quizás sería interesante poder utilizar esta misma idea en Entity Framework 6, porque, al fin y al cabo, todos tenemos por ahí muchas aplicaciones en producción que continúan delegando en este marco de trabajo su acceso a datos. Tras una búsqueda en Google y no encontrar gran cosa, pensé que tampoco sería tan difícil de implementar algo que diera el apaño...

1. Enfocando la solución

Manos a la obra. Conceptualmente, lo que pretendemos hacer es bastante sencillo:
  • Queremos que, en cualquier punto de una consulta LINQ, un desarrollador pueda invocar el extensor TagWith() y especificar una etiqueta personalizada para la misma.

  • A la hora de generar el SQL de la consulta, tenemos que recuperar el valor de dicha etiqueta e insertarla a modo de comentario justo antes del SELECT, como en el ejemplo que veíamos anteriormente.
Seguro que existen muchas formas de implementar algo así, pero mi intención no es meterme en fregados importantes como podría ser la escritura de un proveedor personalizado, o bucear entre los oscuros misterios de la implementación interna de Entity Framework. Necesitamos algo más sencillo, quizás algo hacky, pero que funcione :)

Por ello, la solución propuesta consiste en:
  • Crear un método extensor TagWith() sobre IQueryable, que añadirá a la consulta una "marca" fácilmente identificable para detectar a posteriori que ésta incluye una etiqueta personalizada.

  • A continuación, usando los interceptores de Entity Framework 6, descubrir las queries en las que haya sido introducida la "marca" anterior, extraer la etiqueta y reformular la sentencia SQL para incluir dicha etiqueta como comentario.
Con algo más de detalle, y expresado de forma práctica, la cosa quedaría así:
// 1: Consulta inicial (C#)
var query = _ctx.Friends
.TagWith("Hello world!")
.Where(f => f.Id < 10);

// 2: La llamada a TagWith() añade un predicado predefinido a la consulta,
// de forma que es reescrita de la siguiente manera:
var query = _ctx.Friends
.Where(f=> "!__tag!" == "Hello world")
.Where(f => f.Id < 10);

// 3: Debido a lo anterior, el proveedor genera la siguiente consulta SQL:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Country_Id] AS [Country_Id]
FROM [dbo].[Friends] AS [Extent1]
WHERE (N'!__tag!' = N'Hello world!') AND ([Extent1].[Id] < 10)

// 4: En un interceptor de EF detectamos la subcadena "!__tag!", por lo que sabemos
// que se trata de una consulta etiquetada. Manipulando un poco la cadena,
// transformamos la consulta SQL en la siguiente, donde se incluye la etiqueta como
// comentario y ha desaparecido el predicado falso introducido con TagWith():
-- Hello world!
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Country_Id] AS [Country_Id]
FROM [dbo].[Friends] AS [Extent1]
WHERE ([Extent1].[Id] < 10)
Veamos ahora cómo implementar todo esto.

2. Implementación del extensor TagWith()

Esta parte es la más sencilla. El extensor TagWith() simplemente debe añadir al IQueryable<T> una nueva cláusula where fácilmente reconocible a posteriori:
public static class TagWithExtensions
{
public static string TagMarker = "!__tag!";

public static IQueryable<T> TagWith<T>(this IQueryable<T> query, string tag)
{
var tagConstant = Expression.Constant(TagMarker);
var tagValue = Expression.Constant(tag);
var equalsExpression = Expression.Equal(tagConstant, tagValue);
var predicate = Expression.Lambda<Func<T, bool>>(
equalsExpression, Expression.Parameter(typeof(T))
);
return query.Where(predicate);
}
}
Como se observa en el código anterior, generamos un predicado construyendo un árbol de expresión en el que comparamos dos constantes, "!__tag!" y la etiqueta indicada en la llamada al método.

El resultado de dicha comparación será siempre falso, pero en realidad esto no nos importa porque nunca irá al servidor de datos: eliminaremos este predicado de la sentencia SQL generada justo antes de ejecutarse. Veamos cómo.

3. Modificando la consulta

Entity Framework 6 incluye un interesante mecanismo que nos permite consultar y modificar el SQL generado por el proveedor antes de que sea enviado al servidor de datos: los interceptores de comandos.

Estos componentes, que implementan la interfaz IDbCommandInterceptor, permiten tomar el control antes de ejecutar una consulta para analizar o cambiar el SQL generado. Nosotros nos valdremos de ello para determinar si estamos ante una consulta etiquetada y, en su caso, modificarla para adaptarla a nuestros intereses.
public class QueryTaggerInterceptor : DbCommandInterceptor
{
private readonly ISqlTagger _sqlTagger;

public QueryTaggerInterceptor(ISqlTagger sqlTagger)
{
_sqlTagger = sqlTagger;
}

public override void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> context)
{
do
{
command.CommandText = _sqlTagger.GetTaggedSqlQuery(command.CommandText);
}
while (command.CommandText.IndexOf(
TagWithExtensions.TagMarker,
StringComparison.Ordinal) > -1);
}
}
Ese componente de tipo ISqlTagger que usamos en el interceptor simplemente contiene la operación GetTaggedSqlQuery(), que recibe la cadena de texto con la consulta SQL y la retorna modificada según la siguiente lógica:
  • Si la SQL no contiene la marca "!__tag!", es que estamos ante una consulta no etiquetada, por lo que la dejamos tal cual. En caso contrario, asumimos que la consulta está tageada, por lo que continuamos el procedimiento.

  • Extraemos la etiqueta indicada por el usuario en la llamada a TagWith().

  • Eliminamos de la SQL el predicado completo.

  • Y, por último, fijaos que el proceso se repite siempre qe sigamos encontrando marcas en la consulta, puesto que un desarrollador podría haber incluido más de una llamada a Tagwith() sobre la misma.
La definición de la interfaz ISqlTagger es la siguiente:
public interface ISqlTagger
{
string GetTaggedSqlQuery(string sql);
}
El motivo de utilizar una interfaz es que el formato de la consulta SQL podría variar de un proveedor a otro debido a matices sintácticos, y de esta forma podríamos soportar en el futuro distintas implementaciones. Por ejemplo, el siguiente código muestra cómo sería el taggeador para SQL Server:
public class SqlServerTagger: ISqlTagger
{
public string GetTaggedSqlQuery(string sql)
{
public string GetTaggedSqlQuery(string sql)
{
var indexOfTagConstant = sql.IndexOf(
TagWithExtensions.TagMarker, StringComparison.Ordinal
);
if (indexOfTagConstant == -1)
return sql;

var predicateStartIndex = indexOfTagConstant - 2;
var startOfTagIndex = predicateStartIndex + TagWithExtensions.TagMarker.Length + 8;

var predicateEndIndex = sql.IndexOf("'", startOfTagIndex, StringComparison.Ordinal);
var endOfTagIndex = predicateEndIndex - 1; // Remove the final single quote

var tag = sql.Substring(startOfTagIndex, endOfTagIndex - startOfTagIndex + 1);

var startsWithAnd = CmpStr(sql, predicateStartIndex - 5, "AND (");
var endsWithAnd = CmpStr(sql, endOfTagIndex + 2, ") AND");

string finalSql;
if (startsWithAnd)
{
// Predicate pattern: ... AND (N'__tag' = N'mytag')
finalSql = sql.Substring(0, indexOfTagConstant - 8)
+ sql.Substring(endOfTagIndex + 3);
}
else if (endsWithAnd)
{
// Predicate pattern: (N'__tag' = N'mytag') AND ...
finalSql = sql.Substring(0, indexOfTagConstant - 3)
+ sql.Substring(endOfTagIndex + 8);
}
else
{
// It is the only predicate, so remove the whole "Where" section
finalSql = sql.Substring(0, indexOfTagConstant - 8)
+ sql.Substring(predicateEndIndex + 1);
}

finalSql = AddTagToQuery(finalSql, tag);
return finalSql;
}

private string AddTagToQuery(string sql, string tag)
{
[...] // Inserts the tag in the SQL query
}

private static bool CmpStr(string str, int startIndex, string compare)
{
[...] // Detects if a substring is contained in another string
}
}

Por último, los interceptores podemos registrarlos en el sistema de varias formas: desde el archivo de configuración del proyecto, en la configuración del contexto de datos, o globalmente. Por simplificar, lo haremos de esta última manera, introduciendo el siguiente código en el arranque de la aplicación (Por ejemplo, el global.asax.cs si se trata de una aplicación web):
DbInterception.Add(new QueryTaggerInterceptor(new SqlServerTagger()));

4. ¡Quiero probarlo!

He colgado el proyecto completo en Github por si queréis echar un vistazo al componente o ver algunos ejemplos de uso (en los tests, básicamente). Y para facilitar aún más las cosas, he creado el paquete NuGet EF6.TagWith, que podéis usar sin tener que copiar y pegar nada ;)

Para utilizarlo sólo tenéis que instalarlo en un proyecto donde uséis Entity Framework 6:
PM> install-package EF6.TagWith
A continuación, debéis registrar el interceptor en la inicialización de vuestra aplicación, tal y como hemos visto antes:
DbInterception.Add(new QueryTaggerInterceptor(new SqlServerTagger()));
Y hecho esto, taggear algunas consultas y utilizar alguna herramienta como SQL Profiler para observar el resultado:
var query = ctx.Friends
.OrderByDescending(friend => friend.Age)
.Take(10)
.Select(friend => new { FriendName = friend.Name, CountryName = friend.Country.Name })
.TagWith("Get top 10 older friends with country");

SQL Profiler mostrando queries etiquetadas

Espero que os sea de utilidad :)

Publicado en: www.variablenotfound.com.

Fixed Buffer: La potencia de la Reflexión en C# (Parte 2: Ensamblados)

$
0
0
La imagen muestra el logo de C# para Reflexión Ensamblados

En la última entrada hablábamos sobre un problema que me había encontrado en el trabajo y como la reflexión me había permitido resolverlo de una manera elegante y con poco código. Al menos, muchísimo menos código del que habría necesitado sin haberla usado.

Pensaba hacer simplemente un par de entradas hablando sobre el tema, pero creo que tiene bastante miga, así que lo mejor va a ser hacer una pequeña serie hablando de todas las posibilidades que nos ofrece esta poderosa herramienta. Así que hoy vamos a empezar con la primera de la lista y la que a su vez las contiene a todas: «Assembly».

Assembly (ensamblado): Los ensamblados son los bloques de creación de las aplicaciones .NET Framework; constituyen la unidad fundamental de implementación, control de versiones, reutilización, ámbitos de activación y permisos de seguridad. Un ensamblado es una colección de tipos y recursos compilados para funcionar en conjunto y formar una unidad lógica de funcionalidad. Los ensamblados proporcionan a Common Language Runtime la información necesaria para conocer las implementaciones de tipos. Para la ejecución, un tipo no existe fuera del contexto de un ensamblado.

Fuente: MSDN

¿Y que es un assembly? Pues dicho con palabras sencillas, es la unidad mínima que el CLR puede ejecutar. Contiene las los módulos, las clases, los tipos… y lo más importante, el manifiesto, que es donde se registran todos los metadatos. (Cuando hablemos de los módulos veremos que la principal diferencia es esta última). Cuando una aplicación arranca, el CLR consulta los metadatos del ensamblado para conocer el punto de entrada a este. Él

static void Main(string[] args)

de toda la vida. Mediante reflexión, podemos obtener esos metadatos de el/los ensamblados cargados en la aplicación, pudiendo saber que versión de fichero estamos ejecutando, obtener una lista detallada de todas las clases y métodos que están disponibles en nuestra aplicación, o incluso cargar nuevos ensamblados en nuestra aplicación y permitir que estén disponibles de manera dinámica.

Ejemplos de uso para metadatos

Por ejemplo, un código como este nos permitiría saber la versión que estamos ejecutando:

using System;
using System.Reflection;

namespace PostReflexion
{
    class Program
    {
        static void Main(string[] args)
        {
            var assembly = Assembly.GetAssembly(typeof(Program));
            Console.WriteLine($"Versión: {assembly.GetName().Version}");
        }
    }
}

U obtener todos los tipos (clases, interfaces, enumeraciones….):

using System;
using System.Reflection;

namespace PostReflexion
{
    class Program
    {
        static void Main(string[] args)
        {
            var assembly = Assembly.GetAssembly(typeof(Program));
            foreach (var type in assembly.DefinedTypes)
            {
                Console.WriteLine(type);
            }
        }
    }
}

-Vale, ¿y eso para qué vale? ¡Tampoco es una grandísima utilidad…!

En si mismo, lo visto anteriormente no es de una gran utilidad, ya que para obtener la versión podríamos utilizar «FileVersionInfo» y saber el contenido de un ensamblado en tiempo de ejecución no nos vale de mucho… ¿O sí?

Una herramienta muy potente que nos da la clase Assembly, es que nos permite crear objetos de manera dinámica, es decir, durante el la ejecución del programa, y sin estar explícitamente escrito en nuestro código.

Imagina que tienes una aplicación que sirve de punto de lanzador de otra serie de aplicaciones contenidas en librerías. A simple vista, tienes 3 opciones:

  • Distribuir la aplicación con todas su dll referenciadas, haciendo que gente que no ha pagado por todas las aplicaciones se tenga que descargar un montón de ficheros que no va a poder usar. (Mayor tamaño de archivos)
  • Generar diferentes soluciones que utilicen unas u otras librerías para generar diferentes paquetes de aplicaciones, pero esto crece de manera exponencial con el número de librerías y tipos de «paquete». (Mayor trabajo de mantenimiento)
  • Listar las librerías disponibles dentro del directorio, obtener de ahí las clases que inician las diferentes aplicaciones y ejecutarlas de manera dinámica.

Nota: Cualquiera de las 3 opciones debería contar con un sistema de autenticación que acredite que realmente tiene derecho a utilizar la aplicación.

Creando instancias de manera dinámica

Para ello, aprovechando la opción que nos da la reflexión de instanciar clases, podríamos hacer algo como esto:

var assembly = Assembly.LoadFile("ruta a la librería");
            
//Creamos el objeto de manera dinámica
var formDinamico = assembly.CreateInstance("Nombre completo de la clase") as Form;

//Si hemos podido crear la instancia, abrimos el formulario
formDinamico?.ShowDialog();

Con este código y gracias a assembly, solo cambiando las rutas de cargar la librería y el nombre de la clase, podemos abrir cualquier formulario que herede de Form (WinForms). Esto mismo se puede aplicar a WPF sin ninguna dificultad.

Además, también es posible crear instancias de clases que tienen un constructor con argumentos, pasándole esos argumentos:

//Código de la clase
//=============================================
public class ClaseEjemplo
{
    private int _valor;
    public ClaseEjemplo(int valor)
    {
        _valor = valor;
    }
    public int Multiplicar(int por)
    {
        Console.WriteLine($"Llamada a {nameof(Multiplicar)} con parámetro {por}");
        return _valor * por;
    }
}

//Código para instanciar y ejecutar sus métodos
//=============================================
var assembly = Assembly.LoadFile("ruta a la librería");
//Creamos el objeto de manera dinámica
var objetoDinamico = assembly.CreateInstance("PostReflexion.ClaseEjemplo"
                               ,false
                               ,BindingFlags.ExactBinding
                               ,null
                               ,new object[]{2} //Contructor
                               ,null  
                               ,null);

// Creamos una referencia al método   
var m = objetoDinamico.GetType().GetMethod("Multiplicar");

//Llamamos al método pasandole el objeto creado dinámicamente y los argumentos dentro de un object[]
var ret = m.Invoke(objetoDinamico, new object[] { 3 });
Console.WriteLine($"El retorno de la función es: {ret}");
Console.WriteLine();

No vamos a entrar a ver como hemos llamado a su método, ya que eso lo veremos cuando lleguemos a esa parte, pero el código es funcional y podemos comprobar como hemos instanciado de manera dinámica la clase y le hemos pasado un valor a su constructor, pudiéndolo comprobar al llamar al método.

Estas son solo alguna de las muchísimas opciones que nos da la reflexión en C# a través de la clase Assembly. Combinándola con las demás que vamos a ver, nos permite ejecutar cualquier código de manera dinámica en tiempo de ejecución, lo cual es una herramienta muy potente y que abre un gran abanico de posibilidades.

He actualizado el repositorio de GitHub para añadir los ejemplos de obtención de metadatos y creación de clases de manera dinámica para poder probar esos conceptos.

**La entrada La potencia de la Reflexión en C# (Parte 2: Ensamblados) se publicó primero en Fixed Buffer.**

Picando Código: Actualización de MontevideoBicis con datos nuevos en el mapa: bicicircuitos, bicicletarios, estaciones y talleres de reparación

$
0
0

Montevideo Bicis

Actualicé el sitio MontevideoBicis con datos nuevos. Los datos de accidentes de tránsito no han sido actualizados a 2018 por UNASEV todavía, pero sí los del mapa. La versión más reciente es de marzo de 2018, pero tenía una versión bastante más antigua en el sitio.

Las actualizaciones entonces:

  • Actualizados datos de bicicircuitos, bicicletarios, estaciones y talleres de reparación (fuente). Habría menos calles de 30km/h en Ciudad Vieja que en los datos anteriores, y algunas calles de 30km/h más en el este de la ciudad.
  • El mapa se ve un poco más grande, ocupando el espacio de la pantalla.
  • Actualicé la versión de Ruby a 2.6.3 y las gemas a sus respectivas versiones nuevas.

Por cierto mirando datos, aprendí que GitHub reconoce los archivos .geojson y los muestra en un mapa (Ejemplo).

Usar HAML parecía una buena idea en su momento, pero ahora lo miro y me dan muchas ganas de pasar todo a ERB… y quitar Bootstrap… y darle un diseño más decente… Pero bueno, algún día habrá tiempo.

Los datos de geolocalización son convertidos a geojson para usar en el mapa y los pueden encontrar y reutilizar desde los siguientes enlaces:

Picando Código: Emacs Boostrap: Entornos de desarrollo para Emacs al vuelo

$
0
0

Emacs Bootstrap es un sitio que te permite generar entornos de desarrollo en Emacs para distintos lenguajes de programación: Python, Ruby, Go, PHP, JavaScript, Web, Haskell, Elixir, Rust, Racket y C. Desde la página elegimos qué lenguages queremos usar con Emacs y va a generar las configuraciones necesarias para empezar a escribir código. También nos permite elegir entre 3 temas distintos: solarized-emacs, zenburn-emacs y spacemacs y qué motor de autocompletado preferimos entre ivy y helm.

Emacs Bootstrap

Esto nos genera un archivo con el directorio .emacs.d ya listo para ubicar en nuestro directorio home, ejecutar Emacs, esperar que se bajen los paquetes y empezar a programar.

El entorno es opinionado, por lo que tiene su forma de hacer las cosas. Para eso hay que aprenderse los atajos de teclado y después ir metiendo mano en el archivo init.el si queremos agregarle más cosas.

El autor está buscando extender Emacs-bootsrap para darle soporte a más lenguajes de programación. El código está disponible en GitHub, así que si crees que tenés una configuración perfecta de un lenguaje de programación no incluido, envía un Pull Request.

En lo personal vengo usando Spacemacs hace como 2 años, pero todas estas iniciativas son muy bienvenidas. Sobretodo a la hora de arrancar, para no sentirse abrumado con un archivo de configuración vacío o la configuración por defecto. Recurso agregado a Emacs.sexy🙂

Blog Bitix: Orden de ejecución de las cláusulas de las sentencias SELECT de SQL

$
0
0
PostgreSQL
MySQL

El lenguaje SQL es un potente lenguaje declarativo usado en las bases de datos relacionales como MySQL o PostgreSQL. En el lenguaje SQL se declara que datos se quieren recuperar, qué condiciones han de cumplir cumplir y qué funciones se aplican a los datos pero no se define como han de recuperarse los datos, es la base de datos la que decide como guardarlos e interpretando la sentencia SQL la que decide cómo recuperarlos.

El lenguaje SQL se compone de diferentes tipos de sentencias según el tipo de operación, lectura de datos con SELECT, inserción de datos con INSERT, actualización con UPDATE y eliminación con DELETE.

Las sentencias SELECT tienen la siguiente sintaxis en PostgreSQL y MySQL.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[WITH[RECURSIVE]with_query[,...]]SELECT[ALL|DISTINCT[ON(expression[,...])]][*|expression[[AS]output_name][,...]][FROMfrom_item[,...]][WHEREcondition][GROUPBYgrouping_element[,...]][HAVINGcondition[,...]][WINDOWwindow_nameAS(window_definition)[,...]][{UNION|INTERSECT|EXCEPT}[ALL|DISTINCT]select][ORDERBYexpression[ASC|DESC|USINGoperator][NULLS{FIRST|LAST}][,...]][LIMIT{count|ALL}][OFFSETstart[ROW|ROWS]][FETCH{FIRST|NEXT}[count]{ROW|ROWS}ONLY][FOR{UPDATE|NOKEYUPDATE|SHARE|KEYSHARE}[OFtable_name[,...]][NOWAIT|SKIPLOCKED][...]]wherefrom_itemcanbeoneof:[ONLY]table_name[*][[AS]alias[(column_alias[,...])]][TABLESAMPLEsampling_method(argument[,...])[REPEATABLE(seed)]][LATERAL](select)[AS]alias[(column_alias[,...])]with_query_name[[AS]alias[(column_alias[,...])]][LATERAL]function_name([argument[,...]])[WITHORDINALITY][[AS]alias[(column_alias[,...])]][LATERAL]function_name([argument[,...]])[AS]alias(column_definition[,...])[LATERAL]function_name([argument[,...]])AS(column_definition[,...])[LATERAL]ROWSFROM(function_name([argument[,...]])[AS(column_definition[,...])][,...])[WITHORDINALITY][[AS]alias[(column_alias[,...])]]from_item[NATURAL]join_typefrom_item[ONjoin_condition|USING(join_column[,...])]andgrouping_elementcanbeoneof:()expression(expression[,...])ROLLUP({expression|(expression[,...])}[,...])CUBE({expression|(expression[,...])}[,...])GROUPINGSETS(grouping_element[,...])andwith_queryis:with_query_name[(column_name[,...])]AS(select|values|insert|update|delete)TABLE[ONLY]table_name[*]

La sentencia SELECT se compone de varias cláusulas que se interpretan siguiendo una secuencia de operaciones tal que:

  1. FROM: obtener los registros de todas las tablas fuentes de dato. Si hay subqueries en la cláusula FROM son evaluadas primero.
  2. JOIN: Realizar todas las posibles combinaciones descartando aquellas combinaciones que no cumplen las condiciones JOIN o estableciendo NULL en caso de outer joins.
  3. WHERE: Filtrar las combinaciones que cumplen las condiciones de la cláusula WHERE.
  4. GROUP BY: Construir los grupos basados en las expresiones de la lista de la cláusulas GROUP BY.
  5. HAVING: Filtrar los grupos que cumplen las condiciones de la cláusula HAVING.
  6. SELECT: Evaluar las expresiones de la lista SELECT para seleccionar los datos.
  7. DISTINCT: Eliminar filas duplicadas si se especifica DISTINCT.
  8. UNION, EXCEPT, INTERSECT: Aplicar las operaciones UNION, EXCEPT e INTERSECT.
  9. ORDER BY: Ordenar las filas de acuerdo a la cláusula ORDER BY.
  10. OFFSET, LIMIT: Descartar los registros de acuerdo OFFSET y LIMIT.

Este es el orden general pero el algoritmo del planificador puede optimizar estos pasos realizándose en diferente orden o incluso simultáneamente. Por ejemplo, si se especifica un límite de 1 no es necesario obtener todas las filas de las tablas fuente sino solo una que cumpla la condición WHERE.

Blog Bitix: Sobre la Play Station 4, Play Station 5 y Google Stadia

$
0
0

El Intel NUC que adquirí hace unos meses no es el ideal para jugar sobre todo por su pequeño tamaño y temperaturas, en mi caso como jugador ocasional es suficiente pero unido a que soy usuario de GNU/Linux hace que deba usar Wine o Steam para los juegos con lo que hay algunos juegos que me llaman la atención que no tengo disponibles. La Play Station 4 está ya al final de su ciclo de vida, ya hay noticias de la Play Station 5 y Google Stadia ofrecerá una nueva experiencia de juego ¿comprar una Play Station 4? ¿o seguir con Diablo 3 mientras se lanzan la Play Station 5 o a Stadia?.

Play Station
Google Stadia

Desde que me compré un Intel NUC he estado jugando a Diablo 3 en GNU/Linux con Wine en la distribución Arch Linux perfectamente, al menos de forma jugable con una resolución de 1600x900 en un monitor Benq PD2700Q y con unos detalles de calidad gráfica medios. El NUC con un gráfica Intel integrada Iris 655 es notablemente mejor que la típica UHD 630 de otros modelos de procesador Intel que cumple para un jugador ocasional pero no se puede comparar con una dedicada NVIDIA o AMD de un equipo más grande. Y aunque funciona sin problemas la temperatura que alcanza llega a a entre los 75º y 80º, teniendo en cuenta que es un equipo de 10cm x 10 cm x 3.5cm es una temperatura para la que probablemente no sea recomendable mantener durante largos periodos de tiempo.

Por otro lado el hecho de usar Linux hace que no todos los juegos estén disponibles, con el gran trabajo de Steam hay mayor número de títulos disponibles pero algunos únicamente siguen estando disponibles para Windows como Watch Dogs. Probablemente la mejor alternativa para un usuario de Linux y para mi es usar una consola para tener un catálogo amplio de juegos, sin problemas de compatibilidad y con buen rendimiento. La consola candidata que evaluaría sería una Play Station.

Ya han sido publicadas noticias indicando que la Play Station 4 está al final de su ciclo de vida comercial y será sustituida previsiblemente en algún momento de 2020 o 2021 por una nueva Play Station 5. Por otro lado Google ha anunciado su plataforma de juegos Stadia con un funcionamiento vía streaming, disponible a finales del 2019 o principios del 2020. En esta situación surgen varias dudas razonables: ¿comprar una Play Station 4? ¿seguir con Diablo 3? ¿esperar a la Play Station 5? ¿que ofrece Google Stadia?

Play Station 4

La Play Station 4 fue comercializada a finales del 2013, con su actualización del modelo slim y pro en el 2016. Habiendo ya pasado varios años desde su lanzamiento posee un amplio catálogo de juegos con muy buena calidad. Su precio está entre los 350€ nueva para el modelo slim o 230€ de segunda mano o seminueva en una tienda. Al haber pasado ya unos años hace que muchos destacados juegos se encuentren en unos razonables precios de entre 10€ y 20€.

Aún habiéndose anunciado la Play Station 5 como su sucesora aún están por lanzarse varios títulos importantes, todavía ofrecerá muchas horas de juego durante varios años más aún después de lanzarse la PS5.

Algunos de los juegos más populares son los siguientes: Grand Theft Auto V, Watch Dogs y Watch Dogs 2, Last of Us, Assassins Creed, FIFA, Dirt Rally, Horizon Zero Dawn, God of War, Bloodborne, Far Cry 5, The Witcher 3, Mafia 3, Tropico 5, Fallout 4, Dark Souls, Days Gone, Uncharted, The Division, Nioh, XCOM 2, Little Big Plannet, …

Diablo 3

Como tengo el Diablo 3 al que juego de forma muy esporádica y aún no le he exprimido completamente puedo pasar un tiempo jugando a este juego. Si Diablo 3 tiene algo es que está diseñado para ser extremadamente rejugable con varios personajes, niveles de dificultad, variaciones en los escenarios y mejora de objetos y personajes aún habiendo completado la historia principal.

Con lo que en mi caso podría esperar al lanzamiento de la PS5 o esperar a ver que ofrece Stadia jugando al Diablo 3.

Play Station 5

Entre las noticias que han aparecido sobre la Play Station 5 es que usará nuevamente un procesador y gráfica AMD pero actualizados para ofrecer mucha más potencia. Usará un AMD Ryzen mucho más capaces que los Jaguar de la PS4 y una gráfica Navi ofreciendo una capacidad de cómputo de entre 8 y 10 TFLOPS por los 1,8 de la PS4.

Uno de los problemas de lanzar una nueva consola es que hasta pasado una buena cantidad de tiempo no posee un amplio catálogo de juegos. En el caso de la PS5 al utilizar la misma arquitectura de procesador otra de las características destacadas es la retrocompatibilidad lo que la dotará del mismo catálogo de la PS4 desde mismo momento del lanzamiento, y todos los buenos juegos de la PS4 puedan ser jugados en la PS5.

Google Stadia

Desde hace bastante tiempo la Play Station ha sido la consola dominante sobre la Xbox y las varias de Nintendo. Google ha presentado ya su plataforma de juegos mediante streaming estando disponible a finales del 2019. La ventaja de Stadia es que al estar completamente en la nube no hace falta adquirir hardware, únicamente comprar los juegos con lo que al igual que las consolas se eliminan completamente los problemas de controladores, rendimiento y compatibilidad, dado que se basa en la nube también elimina el tener que descargar pesadas actualizaciones y reducirá sensiblemente los tiempos de carga.

Ofrecerá una modalidad de suscripción para tener acceso a 4K, 60 FPS, HDR y sonido 5.1 pero en la modalidad Básica solo es necesario adquirir los juegos. Dado que se basa en la nube y funciona con streaming requiere conexión permanente de internet y de un buen ancho de banda, un ADSL no soportará 4K y justamente la calidad 720p o 1080p con lo que sería recomendable utilizar fibra o cable de al menos 30 Mbps. Está por ver como afectará la latencia de la conexión a la experiencia de juego que es especialmente sensible a este aspecto.

Por lo demás el precio de suscripción de 10€/mes es recurrente pero teniendo en cuenta que una PS5 costará entre 400€ y 500€ en el momento de lanzamiento el precio de suscripción dan para varios años en lo que costaría comprar una PS5. Esta también por ver el precio de los juegos en Stadia si serán similares a los de las consolas tradicionales o algo superiores y el catálogo de juegos que ofrece en el momento de lanzamiento y pasados unos meses o algún año. Sin embargo, la tendencia a futuro parece indicar que el modelo de Stadia sea el que sigan las consolas o plataformas de juego más tarde o temprano.

Si Stadia ofrece un buen catalogo de juegos y la latencia no da problemas, los precios no son desorbitados y como parece se adelanta al lanzamiento de la PS5 puede suponer un cambio en las reglas del mercado de la consolas y ponerle dificultades a la hasta ahora dominante posición de Play Station.

Switcher HDMI

En el caso de utilizar un monitor que solo disponga de una entrada HDMI tanto para el PC como para una consola se necesita un conmutador o switch HDMI que permita conmutar la entrada sin tener que conectar y desconectar cables. En Amazon hay muchos modelos, uno de los que más me ha gustado ha sido este Conmutador HDMI 2.0 4K@60Hz de 5 entradas que ofrece 4K a 60 Hz con hasta cinco entradas HDMI, una salida y está alimentado por USB.

Bitácora de Javier Gutiérrez Chamorro (Guti): Sale más a cuenta escribir un libro que escribir software

$
0
0

Hace casi un año desde Análisis estático de FileOptimizer, mucho tiempo sin hablar de FileOptimizer y de software. No significa que el proyecto haya sido abandonado, simplemente que no he encontrado nada interesante que decir. Desde septiembre de 2018 con la versión 13.20, he publicado 5 actualizaciones, ahora vamos ya por la 13.70. Los cambios …

Sale más a cuenta escribir un libro que escribir software Leer más »

Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

La entrada Sale más a cuenta escribir un libro que escribir software aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

Blog Bitix: Hemeroteca #15

$
0
0
Hugo

Estos seis primeros meses del 2019 han transcurrido como todos los años anteriores, sigo manteniendo la constancia de publicar dos artículos nuevos y únicos, algunas semanas sólo uno las menos tres.

En cuanto al blog durante este tiempo le he hecho un cambio de diseño para que el contenido quede centrado en la pantalla y tenga más espacio horizontal lo que creo hace más agradable la lectura del texto y los extractos de código se ven mejor con menos necesidad de hacer desplazamiento horizontal.

Para que la publicidad y los ingresos de adsense no se viesen perjudicados y para mejorarlos más aún si cabe he puesto un bloque billboard en la cabecera, he mantenido el leaderboard después del resumen del articulo en aquellos que lo tienen y la publicidad lateral he hecho que sea sticky de manera que permanece mas tiempo visible con la consiguiente mejora en el numero de clic.

Sólo han pasado dos meses con el nuevo diseño pero creo que ha sido un acierto, para la lectura del usuario como para los ingresos de adsense que apreciablemente sigue mejorado respecto al año anterior. Estos meses de verano el tráfico suele descender algo y las visitas ya no me continúan creciendo mes a mes, los mejores meses son los de octubre, noviembre y diciembre con lo que esas fechas podré comparar mejor con las del año pasado. El número de visitas se mantiene más o menos contante y no de crecimiento como semestres anteriores.

En cuanto a los artículos que he escrito han sido sobre las temáticas habituales a las que dedicó este blog. Como desde hace poco estoy pudiendo usar Java 11 (¡por fin!, algo al menos) en el trabajo espero poder escribir algunos artículos con lo que considere destacable, por el momento he actualizado el ejemplo de código de la serie de artículos de GraphQL de los que tengo pendiente publicar alguno más. Los artículos de este semestre han sido los siguientes. En total 47 nuevos.

En la categoría de Java.

En la categoría de GNU/Linux.

Unos sobre algunas de las herramientas de HashiCorp, los que me gustaría seguir ampliando en el futuro.

Varios sobre JavaScript y desarrollo web.

Sobre SQL destacando el dedicado a como hacer paginación correctamente con seek en vez de como habitualmente con ofsset.

Otros que no entran en las categorias anteriores y otro de opinión.

Los dedicados al blog.

Y finalmente uno que estuvo patrocinado.


Variable not found: Enlaces interesantes 368

$
0
0
Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

Variable not found: Un año más, ¡Microsoft MVP!

$
0
0
MVP Award ProgramHace un ratillo he recibido por parte de Microsoft una gran alegría, y necesito compartirla con todos vosotros porque al fin y al cabo sois una parte muy importante del asunto :)

Por noveno año consecutivo (¡nueve años ya, wow!), he sido reconocido Microsoft MVP por las contribuciones realizadas a la comunidad de desarrolladores durante el año pasado.

Es todo un lujo y un honor seguir formando parte de este grupo de locos de la tecnología y apasionados del software, entre los que me precio de contar con buenos amigos y colegas a los que admiro profundamente.

Lo primero, me gustaría volver a agradeceros a todos, amigos y amigas de Variable Not Found, vuestro incondicional apoyo a lo largo de estos años. Sin duda, es lo ha hecho posible llegar hasta este punto, y tener aún cuerda para ir mucho más lejos ;)

Gracias a mis tres niñas por permitirme tantas horas de dedicación a lo que tanto me gusta, asumiendo con deportividad mi ausencia en muchos momentos. De no contar con su comprensión e infinita generosidad, tampoco habría sido posible llegar a este momento 

Me gustaría también agradecer al equipo del programa MVP en Microsoft el trabajo que realizan, y especialmente a Cristina e Irene por su esfuerzo, cercanía y por ponerlo todo tan fácil.

Por último, aprovecho para enviar una afectuosa bienvenida a los MVP que en este momento estáis celebrando vuestro primer nombramiento y desearos que disfrutéis el momento, porque probablemente se convierta en uno de vuestros más preciados recuerdos. Y por supuesto, mi más sincera enhorabuena también a los que repetís galardón: como suele decirse, el mérito no es sólo llegar, sino también mantenerse.

Y ahora, ¡a celebrarlo!

Desarrollador tecleando como un poseso
Source: Gifmanía.

Publicado en: www.variablenotfound.com.

Variable not found: Null coalescing assigment "??=", el nuevo operador de asignación en C# 8

$
0
0
.NET CoreEn esta ocasión vamos a ver una pequeña novedad de C# 8 destinada a mejorar la codificación de un escenario muy frecuente: asignar un valor a una variable si ésta es nula.
Recordad que C#8 está aún en preview, y para usarlo hay que seguir los pasos que vimos en un post anterior.
En otras palabras, esta mejora pretende simplificar implementaciones como las siguientes, donde comprobamos si una variable contiene null y, en caso afirmativo, le asignamos un valor:
...
var defaultValue = ... // lo que sea;
var x = GetSomething();

// Usando un bloque if:
if(x == null)
{
x = defaultValue;
}

// O bien, usando el null coalescing operator:
x = x ?? defaultValue;

La solución propuesta en C# 8 ha sido la introducción del operador null coalescing assignment??=, de forma que cualquiera de los bloques anteriores podría escribirse de la siguiente forma:
x ??= defaultValue;
Para verlo en acción sobre un ejemplo práctico, implementaremos la clásica propiedad con backing field que solemos usar para inicializar propiedades de tipo colección de forma lazy, es decir, sólo cuando vamos a utilizarlas. En versiones de C# anteriores a la 8, sería algo así:
public class Invoice
{
private ICollection<InvoiceLine> _lines;

public ICollection<InvoiceLine> Lines
{
get => _lines ?? (_lines = new HashSet<InvoiceLine>());
set => _lines = value;
}
}
Y ahora, quedará algo más compacto:
    public class Invoice
{
private ICollection<InvoiceLine> _lines;

public ICollection<InvoiceLine> Lines
{
get => _lines ??= new HashSet<InvoiceLine>(); // <- Here!
set => _lines = value;
}
}
En fin, sin ser algo revolucionario, sí es uno de esos pequeños detalles que continúan haciendo de C# un lenguaje cada vez más completo... ;)

Publicado en Variable not found.

Blog Bitix: ¿Donde comprar un ordenador sin Windows o con GNU/Linux?

$
0
0

Hace no tanto tiempo era prácticamente imposible adquirir un equipo sin Windows preinstalado y salvo algunas excepciones en muchos comercios solo incluyen equipos con Windows o macOS. Pero a día hoy si alguien quiere comprar un equipo sin Windows para instalarle GNU/Linux hay varias posibilidades.

Slimbook
VANT

En todos los comercios, grandes superficies e incluso en las pequeñas tiendas especializadas en informática solo hay dos opciones para comprar un portátil o lo compras con Windows incluído o optas por los caros mac de Apple, los ordenadores de marca en formato torre o todo en uno también vienen todos con Windows. Hasta el momento no he encontrado ningún portátil con la posibilidad de adquirirlos con GNU/Linux en las tiendas en las que he estado. En el caso de los ordenadores de torre que montan las pequeñas tiendas a medida es posible adquirirlos sin sistema operativo pero para los usuarios con pocos conocimientos es un problema ya que no saben como instalárselo. Aún así tampoco he visto a ningún pequeño comercio ofrecer el servicio de instalarle alguna distribución de GNU/Linux sin coste al adquirir el equipo o con un coste mucho más reducido que la licencia de Windows.

Sin embargo, desde hace un tiempo es posible adquirir un ordenador con GNU/Linux y sin la licencia de Windows ni su coste, que no tienen nada que envidiar a cualquier otro equipo comercial de una marca más grande. En el caso de portátil están los Slimbook o de VANT que tiene unas características muy notables con el Slimbook PRO X a un precio razonable, con última generación de procesadores Intel, posibilidad de dos SSD NVMe, 32 GiB de memoria, pantalla con resolución 1920x1080 de marcos reducidos. En el caso de Slimbook ofrecen también un modelo de mini PC, el Slimbook ONE o con disipación pasiva el ZERO. En el caso de VANT sus portátiles MOOVE y de escritorio BLOCK, CELL y LIFE inluso con procesadores AMD Ryzen. En ambos casos hay posibilidad de elegir entre algunas distribuciones de GNU/Linux con las que se quiere que venga preinstalado y estos son solo algunos de los modelos que tienen disponibles, Slimbook tiene también algún modelo tipo AIO o all-in-one.

Portátiles Slimbook y VANT
Ordenadores de escritorio Slimbook y VANT

Otra alternativa son los Inte NUC como el Bean Canyon que adquirí hace unos unos meses, al ser un barebone no incluía memoria ni almacenamiento, tanto la memoria como el almacenamiento SSD los adquirí aparte y su instalación realizable en unos pocos minutos es muy sencilla ni requiere grandes conocimiento. Como no incluía almacenamiento tampoco incluía Windows, le instalé Arch Linux y todo el hardware fue reconocido sin necesidad de controladores adicionales.

Los pequeños comercios seguramente no puedan competir en precio con los grandes superficies o gigantes como Amazon o grandes superficies como El Corte Inglés o MediaMarkt pero creo que tienen una oportunidad si ofrecieran el servicio de instalar alguna distribución de GNU/Linux o en ofrecer asesoramiento personalizado.

Blog Bitix: Los tipos de módulos de JPMS añadidos a partir de Java 9

$
0
0

Los módulos de Java dotan a la plataforma de nuevas características. Para garantiza la compatibilidad con las librerías existentes y permitir una transición progresiva de una versión sin módulos a una con módulos la plataforma de módulos de Java define varios tipos de módulos.

Java

Con la publicación de Java 9 el 2017 se introdujo la importante novedad de los módulos que proporciona a la plataforma Java varias características como encapsulación fuerte, interfaces bien definidas y dependencias explícitas. Todos las clases se organizan en paquetes y en Java 9 también en módulos para lo cual fue necesario reorganizar en módulos todos los paquetes de los que se compone la API de Java.

Antes de Java 9 todas las clases se proporcionaban en una lista ordenada de archivos jar, lo que se conocía como el classpath y donde cualquier clase tenía acceso a cualquier otra que se encontrase en él respetando los ámbitos de visibilidad (public, protected, private y package). En la definición de cada módulo en su archivo module-info.java se debe especificar cuales son los módulos requeridos para su funcionamiento no permitiendo la máquina virtual de Java el acceso a ninguna otra clase de otros módulos ni a ninguna clase de los paquetes no exportados.

Para mantener la compatibilidad hacia atrás y hacer más sencillo la transición hacía los módulos, algunas librerías puede que ya no tengan mantenimiento, se pueden dar tres tipos de módulos.

  • Módulos con nombre: están compuestos por las librerías que tienen su definición de módulo en el archivo module-info.java y son colocados en el modulepath al iniciar la aplicación. Únicamente leen los módulos que explícitamente se ha indicado en la definición del módulo con la palabra requires y tiene acceso a los paquetes y sus clases exportados de los módulos requeridos.
  • Módulos automáticos: son las librerías que no tienen una definición de módulo pero que son colocadas en el modulepath. La máquina virtual de Java le asigna un nombre de módulo de forma automática según el nombre de la librería o según la propiedad Automatic-Module-Name del archivo de manifiesto. Leen todos los otros módulos del sistema, los que se encuentren en el modulepath (incluidos todos los otros automáticos) y de la imagen del JDK, y todas las clases del módulo anónimo. Los módulos automáticos son necesarios para que no requerir convertir una librería a un módulo, esto es no requerir convertir a un módulo todo el código ya existente lo cual es un problema ya que muchas librerías ya no tienen mantenimiento. Ya que no tienen una definición de módulo se exportan todos los paquetes.
  • Módulo anónimo: todas las librerías (incluso las que tienen su definición del módulo) que se colocan el classpath forman el módulo anónimo. Leen todos los módulos del sistema y tienen acceso a todos sus paquetes exportados.

Ya se han publicado varios libros que explican detalladamente la modularidad introducida en Java 9. Cualquiera de ellos es una guía completa de la modularidad de Java.

PHP Senior: Fira Code: monospaced font with programming ligatures

Variable not found: Enlaces interesantes 369

$
0
0
Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en: www.variablenotfound.com.

Fixed Buffer: La potencia de la Reflexión en C# (Parte 3: Constructores)

$
0
0
La imagen muestra el logo de C#

Volvemos otra semana más hablando de le reflexión en C#, y hoy toca hablar de los constructores. Como recordatorio, las últimas semanas hemos dado unas pinceladas sobre la reflexión y también hemos visto cómo podemos aprovecharla en los ensamblados.

Hoy vamos a seguir buceando un poco en la potencia que nos aporta la reflexión, en este caso para los constructores. Como vimos, es posible crear instancias de clases mediante reflexión utilizando:

var assembly = Assembly.GetAssembly(typeof(Program));

//Creamos el objeto de manera dinámica
var objetoDinamico = assembly.CreateInstance("PostReflexion.ClaseEjemplo",
                                   false,
                                   BindingFlags.ExactBinding,
                                   null,
                                   new object[] { 2 }, /*Argumentos del constructor*/
                                   null,
                                   null);

Pero esto tiene un mayor coste si vamos a utilizar varias veces el mismo constructor. Esto es porque la reflexión, pese a ser un proceso potente, es caro. En la siguiente tabla se puede comparar los resultados entre instanciar el mismo objeto con el código anterior, o buscando su ConstructorInfo, almacenándolo en memoria y llamando solo a este (no te preocupes, ahora vamos a ver que es el ConstructorInfo).

La imagen muestra la comparativa en benchmark de utilizar el constructor de la clase, Assembly.CreateInstance y ConstructorInfo.Invoke, donde se puede ver que Assembly.CreateInstance tarda de media 1362 ns , ConstructorInfo.Invoke tarda de media 218 ns y el constructor de la clase 3 ns

De los datos de la imagen anterior, se puede ver claramente que el proceso de creación de un objeto es hasta 376 veces más lento usando Assembly.CreateInstance, mientras que usando un ConstructorInfo.Invoke solo lo es 60 veces. esto es porque obligamos a ejecutar todo el proceso de búsqueda del constructor de la clase cada vez que queremos crear un objeto (y recordemos la que reflexión es cara, por eso llamar al constructor de la clase siempre es más rápido).

Una vez vistos los datos, está claro que siempre que podamos, lo mejor es no utilizar reflexión, pero si vamos a usarla y encima de manera repetida, es mejor almacenar las partes que necesitemos y no tener que buscarlas cada vez.

Obteniendo los constructores por reflexión

Vale, llegados a este punto, tenemos una cosa clara:

«Un gran poder conlleva una gran responsabilidad»

Tío Ben – Spiderman

Dentro de Assembly, también podemos obtener las declaraciones de tipos («Type«), donde se encuentra toda la información de la definición:

className = "PostReflexion.ClaseEjemplo";
var assembly = Assembly.GetAssembly(typeof(Program));
var type = assembly.GetType(className);

Y es aquí donde empieza «la magia»… Este objeto «Type» nos permite bucear en todos los recovecos que contiene y obtener entre otras cosas un ContructorInfo por cada constructor que se haya definido en el código (en las siguientes entradas iremos viendo el resto de sus posibilidades).

var className = "PostReflexion.ClaseEjemplo";
var assembly = Assembly.GetAssembly(typeof(Program));
var constructors = assembly.GetType(className).GetConstructors();

O incluso, nos permite acceder al que coincida con el tipo de argumentos (y el orden) que le pedimos:

var className = "PostReflexion.ClaseEjemplo";
var assembly = Assembly.GetAssembly(typeof(Program));
var type = assembly.GetType(className);

//Contructor con parametro int          
var constructorConParametros = type.GetConstructor(new[] { typeof(int) }); 
//Constructor genérico
var constructorSinParametros = type.GetConstructor(Type.EmptyTypes); 

Y sin darnos cuenta, ¡ya tenemos nuestro ConstructorInfo listo! Ahora solo nos queda llamar a su método Invoke pasándole como parámetro un array de objetos que cumpla con la firma, es decir, si el constructor espera un «int», le pasaremos un new object[] {int}, si espera un int y un string, new object[] {int,string} ,etc:

var className = "PostReflexion.ClaseEjemplo";
var assembly = Assembly.GetAssembly(typeof(Program));
var type = assembly.GetType(className);

//Contructor con parametro int
var constructorConParametros = type.GetConstructor(new[] { typeof(int) }); 
//Creamos el objeto de manera dinámica
var objetoDinamicoConParametros = constructorConParametros.Invoke(new object[] { 2 });

Lo que sí es un poco más «raro», es cuando queremos buscar los constructores por reflexión para conseguir el genérico, que hay que utilizar «Type.EmptyTypes» a la hora de buscarlo (y no pasarle nada en el Invoke)

var className = "PostReflexion.ClaseEjemplo";
var assembly = Assembly.GetAssembly(typeof(Program));
var type = assembly.GetType(className);

//Constructor genérico           
var constructorSinParametros = type.GetConstructor(Type.EmptyTypes); 

//Creamos el objeto de manera dinámica
var objetoDinamicoSinParametros = constructorSinParametros.Invoke(new object[] { });

¡Vamos a probarlo!

Para probar que funciona, yo he definido una clase con dos constructores así:

public class ClaseEjemplo
{
    private int _valor;
    public ClaseEjemplo(int valor)
    {
        _valor = valor;
    }

    public ClaseEjemplo()
    {
        _valor = 0;
    }

    public int Multiplicar(int por)
    {
        Console.WriteLine($"Llamada a {nameof(Multiplicar)} con parámetro {por}");
        return _valor * por;
    }
}

Y con este código, voy a llamar a sus dos constructores y y a su método Multiplicar (en las próximas entradas veremos en detalle cómo). Según el código, cuando cree la instancia con el constructor genérico, el resultado de llamar a Multiplicar siempre será 0, ya que cualquier número por 0 es 0:

var className = "PostReflexion.ClaseEjemplo";
var assembly = Assembly.GetAssembly(typeof(Program));
var type = assembly.GetType(className);

//Obtenemos los constructores
//Contructor con parametro int
var constructorConParametros = type.GetConstructor(new[] { typeof(int) }); 
//Constructor genérico
var constructorSinParametros = type.GetConstructor(Type.EmptyTypes); 

//Creamos el objeto de manera dinámica
var objetoDinamicoConParametros = constructorConParametros.Invoke(new object[] { 2 });
var objetoDinamicoSinParametros = constructorSinParametros.Invoke(new object[] { });

// Creamos una referencia al método   
var m = assembly.GetType(className).GetMethod("Multiplicar");

//Llamamos al método pasandole el objeto creado dinámicamente y los argumentos dentro de un object[]
var retConstructorParamatrizado = m.Invoke(objetoDinamicoConParametros, new object[] { 3 });
var retConstructorGenerico = m.Invoke(objetoDinamicoSinParametros, new object[] { 3 });
Console.WriteLine($"El retorno de la función con constructor parametrizado es: {retConstructorParamatrizado}");
Console.WriteLine($"El retorno de la función con constructor genérico es: {retConstructorGenerico}");

Tras ejecutar el programa, efectivamente podemos comprobar que el resultado es el esperado:

La imagen muestra el resultado de hacer llamadas a los constructores mediante reflexión

Con esto hemos visto una manera de crear objetos de manera dinámica «eficientemente» gracias a la reflexión en constructores. En las siguientes entradas, seguiremos desgranando como funciona esta maravillosa herramienta que es la reflexión.

He actualizado el repositorio de GitHub con el código para añadir el ejemplo de reflexión en constructores.

**La entrada La potencia de la Reflexión en C# (Parte 3: Constructores) se publicó primero en Fixed Buffer.**

Variable not found: Tipos referencia anulables en C# 8

$
0
0
.NET CoreCuando, en 1965, el bueno de Tony Hoare introdujo las referencias nulas en el lenguaje ALGOL simplemente "porque era fácil de implementar", no era consciente de que se trataba de un error que a la postre él mismo definiría como su "error del billón de dólares".

De hecho, en el top ten de errores de ejecución de aplicaciones creadas con casi cualquier lenguaje y plataforma, las excepciones o crashes debidos a las referencias nulas son, con diferencia, el tipo de error más frecuente que solemos encontrar.

Pues en este repaso que vamos dando a las novedades principales de C# 8, hemos llegado la que probablemente podría ser la característica más destacada en este entrega, cuyo objetivo es precisamente establecer las bases para que podamos olvidarnos de las referencias no controladas a nulos.
No olvidéis que hasta que sea lanzado oficialmente C# 8, para poder probar sus características hay que hacer algunas cosillas.

¿Permitir valores nulos en tipos referencia?

Ya, pensaréis que todos los tipos referencia son implícitamente anulables en .NET, y de hecho es así.
A diferencia de los tipos valor (int, char, double...), las variables o miembros que contienen tipos referencia no guardan sus valores, sino punteros a los mismos.

Y como punteros que son, pueden contener un valor nulo, razón por la cual el siguiente código es válido:
var s = "Hello, world!"; // La variable "s" apunta a la posición en memoria de la cadena
s = null; // Ahora, "s" no apunta a ningún sitio
El problema es que en tiempo de ejecución nunca sabemos si una determinada variable contiene un nulo, por lo que, si queremos tener una aplicación realmente robusta, deberíamos comprobar continuamente si es así antes de utilizarla:
var s = GetSomeString(); // ¿Retornará null? ¿O tal vez no?
if(s != null)
{
Console.WriteLine(s.Length);
}
Y claro, los disgustos llegan cuando olvidamos introducir dicha comprobación o cuando, por su contexto, intuimos que el valor nunca será nulo, ignorando esa famosa ley que afirma que "si un valor puede ser nulo, el algún momento será nulo" ;)

Para evitar esto, en C# 8 podemos indicar expresamente en qué variables consideramos que null puede ser un valor válido y en cuáles, aun siendo tipos referencia, no vamos a permitir este valor.

De esta forma, conseguimos desplazar al compilador la responsabilidad de realizar dichas comprobaciones, y llevándonos a tiempo de desarrollo la detección de errores debido a este asunto.

Tipos referencia anulables

A partir de C# 8, para indicar que podemos utilizar null en un tipo referencia, debemos indicarlo expresamente añadiendo una interrogación al mismo, de la misma forma que hacíamos con los tipos valor anulables como int? o DateTime?.

La idea es que el siguiente código nos lance errores (o como mínimo warnings) en tiempo de compilación, en lugar de explotar en runtime cuando se intente establecer usar la referencia incorrecta:
string? nullableString = "Nulls are welcome!";
string notNullableString = "Nulls are not welcome!";
nullableString = null; // Allowed
notNullableString = null; // Compilation warning!
Console.WriteLine(nullableString.Length + notNullableString.Length); // Compilation warning!
Así de sencillo :)

Pero cada cosa en su contexto, y dándole la importancia apropiada

Fijaos que el hecho de marcar con la interrogación los tipos referencia anulables implicaría que gran parte de nuestro código actual sería incorrecto, por lo que el uso de esta característica se convertiría en un breaking change de los gordos en los proyectos escritos con versiones anteriores a C# 8.

Por esta razón, se trata de una característica que podemos activar o desactivar a nivel de proyecto y de bloque de código.

Por ejemplo, para indicar que el compilador debe realizar estos chequeos en el proyecto completo, basta con añadir el elemento <NullableContextOptions> al archivo .csproj como sigue:
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NullableContextOptions>enable</NullableContextOptions>
...
</Project>
Los proyectos que no incluyan este elemento, o bien lo establezcan como disable no relizarán ningún tipo de comprobación, por lo que todo funcionará como en versiones anteriores del compilador.
Otra posibilidad es establecer este valor a nivel de bloque de código, para lo cual podemos utilizar la directiva #nullable, como en el siguiente ejemplo:
13: // Activamos el chequeo de nulos...
14: #nullable enable
15: string? nullableString = "Nulls are welcome!";
16: string notNullableString = "Nulls are not welcome!";
17: nullableString = null; // Ok, we allow nulls
18: notNullableString = null; // Warning!
19: Console.WriteLine(
20: nullableString.Length + notNullableString.Length); // Warning!

21: // Ahora volvemos a aplicar la configuración a nivel de proyecto
22: #nullable restore
23: ...
Al compilar un proyecto con este código desde la CLI, el resultado obtenido por consola sería el siguiente:
D:\Test>dotnet build
Microsoft (R) Build Engine versión 16.0.443 para .NET Core
Copyright (C) Microsoft Corporation.
...

Program.cs(18,33): warning CS8600: Converting null literal or possible null value to non-nullable type.
[D:\Test\ConsoleCore2.csproj]
Program.cs(20,17): warning CS8602: Possible dereference of a null reference. [D:\Test\ConsoleCore2.csproj]
Program.cs(20,41): warning CS8602: Possible dereference of a null reference. [D:\Test\ConsoleCore2.csproj]
3 Advertencia(s)
0 Errores

Tiempo transcurrido 00:00:00.76
D:\Test>_
Por defecto, podemos ver que el compilador nos avisará de los problemas encontrados mediante warnings y ya es decisión nuestra si queremos dejarlo así, o si preferimos "ascenderlos" a errores para bloquear la generación de binarios, por ejemplo añadiendo al .csproj el elemento <TreatWarningsAsErrors>true</TreatWarningsAsErrors>, o bien indicando expresamente qué warnings queremos que sean tratados como errores:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
...
</Project>
En cualquier caso, los avisos nos están indicando de que:
  • Hemos establecido a nulo una variable que no lo admite (línea 18).
  • Estamos accediendo a propiedades de una referencia que podría ser nula (línea 20, dos veces).

Uso de tipos referencia anulables

En la práctica, el uso de esta característica nos permitirá crear código muy robusto, pues la comprobación de nulos no se dejará al azar o al criterio del desarrollador: será el compilador el que vele por nuestra seguridad.

Por ejemplo, en el siguiente código, vemos que el método GetPerson() está declarando explícitamente que retornará objetos Person que nunca van a ser nulos. Sin embargo, dado que en uno de los caminos de ejecución es posible que se retorne dicho valor, el compilador lanzará un warning indicándolo:
Person GetPerson(int id)
{
var person = DateTime.Now.Second % 2 == 0? _database.People.Find(id): null;
return person; // Warning CS8603: possible null reference return.
}
En cambio, si gestionamos este valor nulo internamente, el compilador ya no emitirá warnings porque detectará que el método nunca va a devolver null y, por tanto, podemos usar su retorno con total seguridad:
Person GetPerson(int id)
{
var person = DateTime.Now.Second % 2 == 0
? _database.People.Find(id)
: null;
if(person == null)
person = Person.Anonymous;
return person;
}

// Usage:
var person = GetPerson(1);
Console.WriteLine(person.Age); // It's ok, Person is always a valid instance
El compilador también estará atento al caso contrario. Si, por ejemplo, en un método indicamos que retorna un tipo anulable, null será considerado válido y no se generará ningún warning:
Person? GetPerson(int id)
{
var person = DateTime.Now.Second % 2 == 0? _database.People.Find(id): null;
return person; // Ok, null is a valid return value
}
Ahora bien, si un tipo lo hemos marcado como anulable e intentamos acceder a sus miembros sin haber asegurado previamente que no contiene nulos, nos avisará de que hay algo raro:
Person? person = GetPerson(1);
Console.WriteLine(person.Age); // CS8602: Possible dereference of a null reference
La forma de solucionarlo es bien sencilla. Podemos simplemente chequear el valor nulo antes de acceder a sus propiedades con alguna de las construcciones existentes. El siguiente bloque de código no generará ningún tipo de warning o error:
if(person != null)
{
Console.WriteLine(person.Age); // Ok, person is not null
}
Console.WriteLine(person?.Age); // If person is null, "0" will be shown

Null-forgiving operator (!)

Para aquellos casos en los que estamos absolutamente seguros de que un objeto no es nulo y queremos acceder a sus miembros sin necesidad de realizar un chequeo explícito como los vistos anteriormente, podemos usar el operador exclamación "!", aka "olvida los nulos", de la siguiente forma:
Person? person = GetPerson(1);
Console.WriteLine(person!.Age); // Ok, but it could crash in runtime
Básicamente, lo que hacemos con este operador es desactivar justo en ese punto la comprobación de nulos del compilador. O en otras palabras: estamos asegurando al compilador que ahí nunca habrá un nulo (¡y si lo hay, es responsabilidad nuestra!)

Reflexiones finales

Probablemente estemos ante una de las novedades más revulsivas introducidas al lenguaje en los últimos tiempos (bueno, exceptuando quizás el famoso operador virgulilla ;D).

Aunque su impacto ha sido bastante suavizado al permitir su opcionalidad e incluso su adopción progresiva, probablemente a la larga acabará calando en nuestras aplicaciones y evitará gran parte de los frecuentes null reference exception que sufrimos a día de hoy. El "a la larga" se debe a que, por las implicaciones que tendría introducir esta característica en código existente, en la mayoría de ocasiones lo más práctico será usarla en proyectos nuevos.

Iremos viendo...

Publicado en Variable not found.

Picando Código: Spacemacs: entorno integrado eficiente y sofisticado para Emacs

$
0
0

Desde hace unos 2 años vengo usando Spacemacs como editor de texto para programar, escribir y más. Lo he mencionado varias veces en el blog particularmente en posts sobre Emacs, pero nunca escribí al respecto. Es un proyecto que me ha dado mucho y me gustaría compartir lo que he aprendido, y con suerte animar a alguien más a probar Spacemacs. Así que ¡hablemos de Spacemacs!

Spacemacs

¿Qué es Spacemacs?

Spacemacs es una «distribución» de Emacs. Emacs es un editor de texto extensible y personalizable. Cada uno puede crear su propia «distribución» agregando funciones, paquetes y distintas configuraciones en archivos Elisp (Emacs Lisp), para que Emacs se comporte o se vea como uno prefiera. En sí Spacemacs es un conjunto de archivos elisp. Si bien hay varios kits de inicio de Emacs para mejorar su funcionamiento por defecto, Spacemacs es uno de los más populares. Se caracteriza entre otras cosas por tener un desarrollo comunitario de software libre bajo licencia GPLv3.

Cuando alguien tiene un problema en Spacemacs, no lo arregla sólo para su caso particular, la solución o nueva característica es compartida con el resto en el marco en común y opinionado que es Spacemacs. Es sumamente modular, no necesitamos usar TODO lo que nos provee, de hecho su perfil altamente configurable nos deja activar y desactivar cosas a medida que las precisamos. Y es muy personalizable.

Dedicada a los usuarios de Vim

Una característica fundamental de Spacemacs es que comenzó como una distribución basada en Evil, una capa extensible de vi para Emacs. Esta herramienta emula las características principales de Vim en Emacs. Gracias a Evil, podemos aprovechar lo bueno de Vim y lo bueno de Emacs combinado en un editor que provee lo mejor de ambos mundos. Ésta fue la razón principal por la que empezamos a usar Spacemacs en mi trabajo en su momento. Había usuarios de Emacs y usuarios de Vim, y con Spacemacs podíamos hacer pair programming en la máquina de cada uno en un entorno familiar. Si bien creo que soy el único que quedó usando Spacemacs, migré mis preferencias particulares de mi configuración de Emacs a Spacemacs y nunca volví atrás. Por más que no necesite usar Vim en mi Emacs, la distribución alcanzó ser mucho más que la combinación de Emacs con Vim.

¿Y qué tiene de bueno?

Viniendo de Emacs, una cosa que me encantaba era agregar funciones que me encontraba por ahí, configuraciones, paquetes nuevos y personalizaciones. En Spacemacs me siento más seguro de hacerlo al poder trabajar dentro en un «framework». Y no es casualidad, Spacemacs se basa en varios principios:

Ergonomía– la forma de usar comandos y atajos está definida con la comodidad y salud de nuestros dedos y manos en mente. En lo personal no sé si exploto esta característica del todo porque no uso el modo Vim que tiene un modo de inserción de texto y otro de ejecución de comandos. Como uso modo Emacs, sigo apretando mucho Ctrl y Alt todo el tiempo 😛

Mnemotecnia– Los atajos de teclado están organizados con prefijos mnemónicos como b para buffer, p para proyecto, etc., por lo que son fáciles de aprender y recordar.

Visible– Visibilidad en tiempo real sobre los comandos y atajos de teclado disponible. Y teniendo una idea de cómo usarlo, es fácil encontrar y descubrir nuevas funcionalidades.

Consistencia– Dadas ciertas convenciones, funciones similares tienen los mismos atajos de teclado en todos lados. Por ejemplo el «ir a definición» de una clase o módulo sería el mismo atajo para Ruby que para Python, o algo así.

Una ventaja que ya tenía Emacs para mí, y se transporta a Spacemacs es que uso una sóla herramienta para todo. Puedo programar en Ruby, Elixir, editar archivos de configuración, ejecutar tests, compilar, usar Git, jugar Tetris, etc. Con el soporte para Language Server Protocol, se puede usar casi cualquier lenguaje de programación con las características de un IDE (auto-complete, documentación). Es un excelente punto de inicio para principiantes que quieran empezar a usar Emacs. En el fondo sigue siendo el mismo editor de texto que vengo usando hace años, y todo lo que se puede hacer en Emacs, se puede hacer en Spacemacs.

¿Cómo se usa?

Spacemacs nos permite acceder a modos comunes y herramientas disponibles en Emacs a través de unidades auto-contenidas que llama capas («layers»). Éstas capas son paquetes de archivos de código elisp que incluyen configuraciones, atajos de teclado, funciones y otros paquetes. Un ejemplo es la capa ruby que incluye un montón de paquetes como rake, bundler, chruby, rvm, y más herramientas que nos ayudan a trabajar con el lenguaje de programación Ruby, y las integra sin tener que instalar cada una o configurarlas a mano. Generalmente podemos personalizar y configurar todo más a fondo, pero las convenciones son suficientemente buenas como para empezar a trabajar con una nueva capa sin mucho esfuerzo.

Es importante recordar que tanto Emacs como Spacemacs son editores de texto. Se usan para mucho más que programación. Varios autores lo usan para escribir. Un ejemplo famoso es Neal Stephenson, autor de Snow Crash, The Diamond Age, entre otros.

Por defecto, Spacemacs usa un archivo ~/.spacemacs para controlar qué capas cargar y personalizar todo. Dentro de este archivo hay varias funciones de Spacemacs donde podemos especificar y personalizar distintos aspectos. Es el archivo que nos conviene guardar bajo control de versiones para ir modificando y mejorando con el tiempo. Pueden leer más al respecto en la guía rápida. Pero si hay algo en particular de lo que les gustaría leer en español sobre Spacemacs, no duden en pedirlo en los comentarios de este post.

En mi caso, como empezamos compartiendo una configuración de Spacemacs, teníamos un archivo .spacemacs bajo control de versiones en un repositorio de la empresa. Y para mis configuraciones personales, escribí una capa personal. Escribí más al respecto en el blog de Cultivate (en inglés), pero en algún momento voy a traducir y actualizar ese post para publicarlo acá.

Cómo aprender a usar Spacemacs

Como mencioné al principio, Spacemacs está basado en Evil, que es un modo que nos permite usar la configuración de teclas de Vim. Es así que existen dos modos principales: Evil Mode (Vim) o Holy Mode (Emacs) y un modo híbrido. Otra característica fundamental de Spacemacs es la tecla líder. En el caso de Evil Mode es SPC y en Holy Mode Alt + M. Esto nos permite ejecutar varios comandos de Spacemacs mismo. La tecla también es modificable. Pero en la documentación siempre vamos a ver SPC para referirse a la tecla líder. Por ejemplo con SPC m (Alt m m en Holy Mode), podemos encontrar todos los comandos del modo principal activado.

Y para hacer las cosas todavía más fáciles, Spacemacs incluye un tutorial propio, al que podemos acceder con SPC h Y para ingresar a un Vimtutor adaptado a Evil que también beneficiará a usuarios en modo Emacs.

Instalación de paquetes

Viniendo de Emacs, nos podemos ver tentados a instalar un paquete con package-install o desde el menú list-packages. Pero al reiniciar Spacemacs, los paquetes instalados «A la Emacs» se van a desinstalar por ser considerados «huérfanos». Para gestionar los paquetes, tenemos que agregarlos al archivo .spacemacs, ya sea agregando una capa que lo use, o agregando un paquete en particular. Al iniciar Spacemacs, se va a fijar los paquetes que tiene que instalar o desinstalar basado en nuestra configuración.

master vs develop

Recomiendo usar la rama develop del repositorio Git, porque master se actualiza muy poco y el desarrollo periódico se va haciendo en develop. El último «release estable» fue en Enero de 2018, pero la rama master se mantiene actualizada con bugs (en teoría). Vengo usando develop casi desde un principio y no he tenido problemas. Pero teóricamente puede tener regresiones.

Conclusión

Gracias por leer hasta acá, espero haber despertado su interés por Spacemacs o que al menos lo tengan más presente como opción si no lo tenían ya. Mis archivos de configuración están compartidos en GitHub, por si quieren ver más. Aunque más adelante voy a contar un poco sobre lo que hay ahí en otro post.

Me gusta mucho seguir usando una herramienta tan extensible como Emacs desde hace años, siempre se aprende algo nuevo. En base a una pregunta en Twitter, veo que no soy el único que se queda con lo mismo. Hasta encontré más gente que usa Spacemacs 🙌🏻. Me interesa saber cómo otras personas usan su editor de texto preferido y qué les lleva a cambiar de uno a otro. Así que si tienen algún comentario al respecto, les agradezco lo compartan 🙂

Entiendo que mi relación con Emacs es un poco personal. Pero también le veo ventaja a aprender una sóla herramienta muy bien que después puedo seguir usando a pesar de cambiar de tecnología o lenguaje de programación. Ni que hablar no tener que volver a aprender atajos de teclado o procesos de trabajo con otro editor de texto. Pero todos somos distintos y siempre lo mejor es tener opciones (incluso con Emacs, cada persona puede tener un entorno sumamente personalizado). Además que cambiar de un editor a otro puede ser un desafío interesante y entretenido, además de darnos la habilidad de aprender nuevas herramientas. Y uno mismo va cambiando con el tiempo, lo que me sirve hoy puede no servirme mañana.

En fin, temas interesantes para charlar.

Les invito a probar Spacemacs, y cualquier duda que tengan pueden contactarme que prometo ayudar dentro de lo que pueda.

Post escrito con Spacemacs✌

Blog Bitix: Recuperar datos eficientemente en GraphQL usando batching con data loaders

$
0
0

Al diferencia de una API REST donde cada recurso posee un endpoint propio en GraphQL los recursos están relacionados y forman un grafo. Por otro lado las propiedades devueltas en una consulta de GraphQL son las que se indiquen en la consulta en vez de prefijadas como en una API REST. Hay que tener en cuenta que GraphQL para recuperar las propiedades de las entidades usa un resolver y las recupera una a una, si se devuelve una lista de elementos y de cada uno de esos elementos otra propiedad para la que hay que generar una consulta adicional a la base de datos el rendimiento no será bueno. Los data loaders permiten recuperar las propiedades relacionadas de una colección de entidades eficientemente evitando el problema 1+N.

GraphQL
Java

Una de las dificultades a resolver en GraphQL es evitar los problemas de generar 1+N consultas dado que en algunas peticiones se recupera una lista de elementos para recuperar alguna otra propiedad de esos elementos para la que se realiza otra consulta. Suele ocurrir al navegar las relaciones de las entidades, por ejemplo al solicitar una lista de libros y de cada libro obtener su autor, para obtener los libros se necesita una consulta y hay que evitar que para recuperar el autor de cada libro generar otra consulta, si el número de libros recuperados es grande el número de consultas será grande y la consulta será poco eficiente, lenta y generará una carga a evitar en el servidor de base de datos.

En el artículo Recuperar datos eficientemente en GraphQL usando batching comentaba una estrategia para evitar este problema que consistía en dados una serie de elementos recuperados y si la propiedad estaba presente en la consulta se obtenían los identificativos de esos elementos y se recuperaba la propiedad para todos los elementos en una única consulta.

Sin embargo, GraphQL posee otra estrategia para resolver el problema de los 1+N, mediante Data Loaders. Para usar un data loader en una propiedad de un tipo hay que crear una clase que implemente la interfaz MappedBatchLoader o MappedBatchLoaderWithContext de java-loader. El método a implementar es load(Set<K>) que recibe un conjunto de instancias de las que se quiere recuperar la propiedad y devuelve un Map<K,V> cuya clave es la instancia de la colección y el valor de la propiedad recuperada.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
packageio.github.picodotdev.blogbitix.graphql.dataloader;importio.github.picodotdev.blogbitix.graphql.type.Book;importorg.dataloader.BatchLoaderEnvironment;importorg.dataloader.MappedBatchLoaderWithContext;importorg.springframework.stereotype.Component;...@ComponentpublicclassIsbnDataLoaderimplementsMappedBatchLoaderWithContext<Book,String>{publicIsbnDataLoader(){}@OverridepublicCompletionStage<Map<Book,String>>load(Set<Book>books,BatchLoaderEnvironmentenvironment){Map<Book,String>isbns=books.stream().collect(Collectors.toMap(Function.identity(), Book::getIsbn));returnCompletableFuture.supplyAsync(()->isbns);}}

Una vez definidos los data loaders hay que incluirlos en un registro e indicarlos en la clase del contexto de GraphQL. El método contextBuilder recibe todas las instancias de data loaders, el método dataLoaderRegistry crea el registro y finalmente se asigna el registro al contexto. Los data loaders cachean los datos de modo que si los datos no se deben compartir entre peticiones hay que construir los data loaders en cada petición.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
packageio.github.picodotdev.blogbitix.graphql;...@SpringBootApplicationpublicclassMain{...@BeanpublicGraphQLContextBuildercontextBuilder(List<MappedBatchLoaderWithContext<?,?>>mappedBatchLoaders){returnnewGraphQLContextBuilder(){@OverridepublicGraphQLContextbuild(HttpServletRequestrequest,HttpServletResponseresponse){graphql.GraphQLContextdata=graphql.GraphQLContext.newContext().build();GraphQLContextcontext=newDefaultGraphQLContext(data,request,response);context.setDataLoaderRegistry(buildDataLoaderRegistry(mappedBatchLoaders,context));returncontext;}@OverridepublicGraphQLContextbuild(Sessionsession,HandshakeRequestrequest){graphql.GraphQLContextdata=graphql.GraphQLContext.newContext().build();GraphQLContextcontext=newDefaultGraphQLContext(data,session,request);context.setDataLoaderRegistry(buildDataLoaderRegistry(mappedBatchLoaders,context));returncontext;}@OverridepublicGraphQLContextbuild(){graphql.GraphQLContextdata=graphql.GraphQLContext.newContext().build();GraphQLContextcontext=newDefaultGraphQLContext(data);context.setDataLoaderRegistry(buildDataLoaderRegistry(mappedBatchLoaders,context));returncontext;}};}privateDataLoaderRegistrybuildDataLoaderRegistry(List<MappedBatchLoaderWithContext<?,?>>mappedBatchLoaders,GraphQLContextcontext){DataLoaderRegistryregistry=newDataLoaderRegistry();for(MappedBatchLoaderWithContext<?,?>loader:mappedBatchLoaders){registry.register(loader.getClass().getSimpleName(),DataLoader.newMappedDataLoader(loader,DataLoaderOptions.newOptions().setBatchLoaderContextProvider(()->context)));}returnregistry;}publicstaticvoidmain(String[]args){SpringApplication.run(Main.class,args);}}

Una vez creados los data loaders hay que usarlos en los resolver de las propiedades de una entidad en la que se desee que se cargue de forma batched. El método de la propiedad del resolver debe devolver un CompletableFuture, el método recibe la instancia de la que se quiere recuperar una propiedad y una referencia de DataFetchingEnvironment de la librería graphql-java, se recupera el data loader de esa propiedad y se le indica que acumule el conjunto de instancias de las que se quiere recuperar. GraphQL en algún momento llamará al método load(Set) que recibe un conjunto de instancias para realizar la carga de todas en una única consulta.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
packageio.github.picodotdev.blogbitix.graphql.resolver;...publicclassBookResolverimplementsGraphQLResolver<Book>{...publicCompletableFuture<String>getDataLoaderIsbn(Bookbook,DataFetchingEnvironmentenvironment)throwsInterruptedException{DataLoader<Book,String>dataLoader=environment.getDataLoader(IsbnDataLoader.class.getSimpleName());returndataLoader.load(book);}...}

Al obtener los datos del conjunto de libros que utilizan un batch loader se produce la siguiente salida.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ curl -XPOST -H 'Content-Type: application/json' -d '{"query":"query{books{id title dataLoaderIsbn}}"}' http://localhost:8080/graphql
{
"data": {
"books": [
{
"id": 7,
"title": "Ojo en el cielo",
"dataLoaderIsbn": "3a8441b1-fd9a-40d4-8765-2ccb0900a223"
},
{
"id": 8,
"title": "Muerte de la luz",
"dataLoaderIsbn": "06f217f7-5be0-4334-b946-1b29e93387a1"
},
{
"id": 9,
"title": "El nombre de la rosa",
"dataLoaderIsbn": "ffc704a3-b34a-4a06-80e1-506e33d20aab"
},
{
"id": 10,
"title": "Los tejedores de cabellos",
"dataLoaderIsbn": "21b70880-e245-405e-bffc-ab6c5975d837"
},
{
"id": 11,
"title": "Ready Player One",
"dataLoaderIsbn": "18cd56d3-8050-4467-9fa8-fb9aeb0d1ea5"
}
]
}
}

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

Picando Código: Comunicación y sincronización entre tu teléfono Android y tu computadora Linux con KDEConnect

$
0
0

Se viene la convergencia entre computadora de escritorio y teléfono móvil, y KDEConnect la hace sentir más cercana. KDEConnect te permite conectar tu computadora con GNU/Linux y tu teléfono o dispositivo móvil con Android. Esto nos permite tener sincronización entre ambos dispositivos y podemos hacer varias cosas: controlar la computadora desde el teléfono, compartir directorios, hacer que suene el teléfono si no lo encontramos, ver notificaciones del teléfono en nuestra computadora y más.

Connect

Es súmamente práctico y funciona muy bien. Lo conocí hace un par de años probando KDE Plasma 5, y hace poco me vino a la memoria de nuevo. Soy de los que se niega a usar servicios de streaming para escuchar música. Me gusta mucho escuchar música y tener que depender de una conexión a internet para eso me resulta absurdo. Por eso sigo comprando CD’s y pasándolos a digital o comprando música para descargar en sitios como Bandcamp. Además arrastro archivos de música desde hace años. Uso mi teléfono para escuchar música y además tengo un pendrive exclusivo para música. A veces me acuerdo de llevar el pendrive del trabajo a casa y viceversa, otras no. Por eso en general el pendrive queda en el trabajo, y en casa escucho música desde el teléfono conectado ya sea con auriculares o a través de un parlante Bluetooth.

Resulta que hace poco estaba en el trabajo y me había olvidado el pendrive con música en casa. Tenía muchas ganas de escuchar un disco en particular, que tengo en el teléfono, pero no en la computadora. Tampoco tenía el cable para transferir los archivos del teléfono a la laptop, y fue ahí que pensé «qué práctico sería mandar los archivos del teléfono a la laptop por algún medio inalámbrico». Y recordé que existía KDEConnect.

KDEConnect es parte del entorno de escritorio plasma, pero funciona en KDE, GNOME y cualquier otro ambiente de escritorio de Linux. En GNOME particularmente existe la extensión GSConnect, una implementación completa de KDEConnect que se integra con Nautilus, Firefox y Chrome. La aplicación para Android se puede descargar desde la Google Play Store y la tienda de aplicaciones libres y abiertas F-Droid.

Una vez instaladas ambas partes, podemos sincronizar desde nuestro teléfono dispositivos que se encuentren en la misma red:

KDEConnect dispositivos

Al encontrar nuestra computadora, la seleccionamos y veremos que no están sincronizadas, por lo que pedimos un «pairing»:

KDE Connect - pairing

Inmediatamente veremos una notificación en nuestro sistema avisándonos que un dispositivo móvil quiere sincronizarse con nuestra computadora. Al aceptarlo, vamos a ver el ícono de KDEConnect en la bandeja de sistema junto al ícono de Wifi y demás:

KDEConnect GNOME

Como ven, nos muestra la información de la carga de batería, y opciones de usar la mensajería del teléfono, hacerlo sonar, ver archivos, compartir cosas y demás. En la App podemos ver las distintas opciones:

KDEConnect App

Como ven en esta imagen, algunas cosas necesitan permisos específicos del teléfono, por lo que tenemos que habilitarlas individualmente. El control remoto funciona increíblemente bien. Podemos usar la pantalla táctil del teléfono como touchpad, e ingresar texto a través del teclado. También podemos usar un menú de presentaciones para pasar «diapositivas» (¿alguna palabra mejor para este término heredado de la era PowerPoint?) con unos botones enormes o si bloqueamos el teléfono con las teclas de volúmen.

Controlar la música que se reproduce en la computadora desde el teléfono tiene su gracia, y me hizo acordar a aquella vez que hice mi propio control remoto para volúmen en Ruby. Para probarlo abrí Clementine, me puse a reproducir un disco, y al ver el control multimedia en KDEConnect en mi teléfono, cargó hasta la tapa del disco que estaba escuchando:

KDEConnect - Música

La aplicación soporta más cosas de las que recuerdo en el momento que la probé por primera vez. En ese momento la desinstalé después de probarla, pero esta vez se queda en mi teléfono y computadora. Cuando la precisamos, resulta extremadamente práctica.

La lista completa de funcionalidades al momento:

  • Recibir notificaciones de tu teléfono en tu computadora y responder a mensajes
  • Controlar la música que estás reproduciendo en tu computadora desde tu teléfono
  • Usar tu teléfono como control remoto de tu computadora
  • Ejecutar comandos predefinidos en tu PC desde dispositivos conectados
  • Ver la carga de la batería de tu teléfono desde la computadora
  • Hacer que suene tu teléfono si no lo encuentras
  • Compartir archivos y enlaces entre dispositivos
  • Navegar los archivos de tu teléfono desde tu computadora
  • Controlar el volúmen de tu computadora desde el teléfono
Viewing all 2715 articles
Browse latest View live