cerrar-sesion editar-perfil marker video calendario monitor periodico fax rss twitter facebook google-plus linkedin alarma circulo-derecha abajo derecha izquierda mover-vertical candado usuario email lupa exito mapa email2 telefono etiqueta

400440402. LINQ Continuo

Escrito por Redacción en Tema de portada
no hay comentarios Haz tu comentario
Imagen de logotipo de facebook Imagen de logotipo de Twitter Imagen de Logotipo de Google+ Imagen de logotipo de Linkedin

Para aquellos que no han estado prestando mucha atención a la andanada de nueva tecnología de Microsoft, LINQ es una de las nuevas características clave de la plataforma 3.5 de .NET. LINQ, abreviatura de “Language Integrated Query,” nos permite expresar tareas de filtrado, clasificación, agrupamiento y agregación utilizando el lenguaje que queramos, con una sintaxis que resulta bastante familiar a cualquiera con experiencia en escribir consultas SQL para bases de datos relacionales.

Da igual el tipo de aplicación que estemos escribiendo, o el sector de la industria al que vaya destinada, siempre hay unas tareas básicas que casi todo programador tiene que hacer. En algún momento, probablemente tendremos un listado de datos que tenemos que clasificar, filtrar, agrupar o agregar de alguna forma.

Antes de LINQ, ello implicaba la tediosa tarea de crear un bucle para iterar por todos los elementos del listado, y después añadir los elementos que hacen corresponder un filtro con la colección de salida, asegurándose de que se mantiene el orden correcto de clasificación. Al usar LINQ, podemos escribir sentencias que se parecen bastante a lo siguiente, simplificando así enormemente el código:


var results =

from customer in CustomerList

where customer.Age > 21

orderby customer.LastName

select customer;

Con la magia de las extensiones de lenguaje que aparecen en .NET 3.5, esta consulta se convertirá esencialmente en algo que se parece a lo siguiente:

IEnumerable results =

CustomerList.Where( f => f.Age >

21).OrderBy( o=>

o.LastName ).Select( s => s);

No hay que preocuparse si la sintaxis nos parece fea – ese es todo el propósito de insertar atajos de lenguaje para las consultas LINQ directamente en C# y VB.NET. Lo que vemos en la muestra de código anterior son extensiones de lenguaje y funciones lambda. La extensión de lenguaje Where a IEnumerable nos permite filtrar prácticamente cualquier listado, siempre que los elementos del listado implementen IEquatable.

((Da igual el tipo de aplicación que estemos escribiendo, o el sector de la industria al que va destinada, siempre hay unas tareas básicas por hacer))

En sí mismo, LINQ es un fantástico añadido al arsenal de cualquier desarrollador que escriba virtualmente cualquier tipo de aplicación. El auténtico poder de la plataforma 3.5 de .NET viene con nuestra capacidad para potenciar la funcionalidad de LINQ utilizando extensiones de lenguaje. Las extensiones de lenguaje nos permiten crear nuestras propias implementaciones anuladas para Where, OrderBy, GroupBy y similares. LINQ Continuo (CLINQ) es sólo un ejemplo de este tipo de extensiones a LINQ que se creó utilizando este servicio.

Cuando se compilan aplicaciones de bases de datos, el modelo general es escribir una consulta para obtener unos datos. Los datos pueden ser modificados después para ser posteriormente enviados de nuevo a la base de datos. Pero en general, no esperamos obtener actualizaciones continuas de la base de datos como datos en el cambio subyacente de tablas. Con la mayoría de las bases de datos relacionales, una buena respuesta haría que la base de datos se detuviera y probablemente impactaría también a la aplicación.

Otras aplicaciones tienen que ser actualizadas rápidamente como respuesta al cambio continuo de datos. Un ejemplo del sector de las finanzas sería una aplicación que escucha los movimientos de los datos del mercado. Con la actual implementación de LINQ, podríamos escribir una consulta como esta:

var tickData = from tick in MarketData.Ticks

where tick.Symbol == “AAPL”

orderby tick.Ask descending

select tick;

El problema con esta implementación es que tickData está totalmente pasado. Inmediatamente después de ejecutar la consulta, tickData no va ni a crecer ni a disminuir de forma dinámica por sí sola. Sería estupendo si los contenidos de tickData se actualizaran de forma continua cada vez que se añaden nuevos datos del mercado a la fuente original de la consulta, MarketData.Ticks. Eso es lo que hace la extensión LINQ Continuo.

LINQ Continuo funciona añadiendo oyentes, o adaptadores, a la colección de fuentes cuando se invocan las extensiones de lenguaje Where, OrderBy, y GroupBy. En algún momento en el futuro, podría soportar otras extensiones también, pero éstas eran las más importantes. La tarea del adaptador es escuchar los cambios en la colección de fuentes, incluidos todos los elementos de la misma, y decidir si ese cambio necesita ser propagado a la colección de destino.

((Otras aplicaciones tienen que ser actualizadas rápidamente como respuesta al cambio continuo de datos))

Por ejemplo, usando la consulta anterior de datos de mercado, si MarketData.Ticks se derivaba de ContinuousCollection, en ese caso se crearían adaptadores que estuvieran pendientes de los datos de la fuente. Si se añade un nuevo elemento a la colección de fuentes que tenga un nombre de símbolo de “AAPL,” en ese caso se añade el elemento automáticamente a los resultados. Estos adaptadores también mantienen el orden de clasificación y pueden anidarse y encadenarse a voluntad. La Figura 1 muestra el flujo de datos por los adaptadores desde una típica consulta CLINQ.

Podemos ampliar cualquier clase IEnumerable derivativa y proporcionar nuestras propias implementaciones específicas de dominio para los operadores de consulta LINQ where, orderby, y groupby. El Listado número 1 ilustra las extensiones where y orderby a una clase denominada ContinuousCollection. Esta clase no es realmente nada más que una derivativa segura para hilos de ObservableCollection que garantiza que todas las publicaciones de los cambios tienen lugar en el hilo principal del expedidor GUI para permitir que todas las GUI WPF y adaptadores CLINQ vean los cambios.

//listado 1

public static class ContinuousQueryExtension

#region Where

public static ContinuousCollection Where(

this ContinuousCollection source, Func filterFunc) where T: IEquatable

Trace.WriteLine("Filtering Observable Collection.");

ContinuousCollection output = new ContinuousCollection();

FilteringViewAdapter fva =

new FilteringViewAdapter(source, output, filterFunc);

return output;

#endregion

#region OrderBy

public static ContinuousCollection OrderBy(

this ContinuousCollection source, Func keySelector)

where TSource : IEquatable

where TKey : IComparable

Trace.WriteLine("Ordering Observable Collection (Ascending).");

ContinuousCollection output = new ContinuousCollection();

SortingViewAdapter sva = new SortingViewAdapter(

source, output,

new FuncComparer(keySelector, false));

return output;

#endregion

#region OrderByDescending

public static ContinuousCollection OrderByDescending(

this ContinuousCollection source, Func keySelector)

where TSource : IEquatable

where TKey : IComparable

Trace.WriteLine("Ordering Observable Collection (Descending).");

ContinuousCollection output = new ContinuousCollection();

SortingViewAdapter sva = new SortingViewAdapter(

source, output,

new FuncComparer(keySelector, true));

return output;

#endregion


En el constructor de los adaptadores, el adaptador adjunta gestores de eventos para los eventos en los que ha cambiado la colección y los eventos en los que ha cambiado la propiedad, para cada elemento de la colección. A continuación, como respuesta a estos cambios, el adaptador determina si el elemento cambiado necesita ser propagado a la colección de destino o si necesita ser eliminado de la colección de destino (por ejemplo, un elemento modificado ya no satisface el predicado de consulta y hay que eliminarlo). La eliminación bajará la cadena en cascada por lo que no importa por cuántos adaptadores where, groupby, o orderby tiene que pasar el cambio.

Lo bueno de CLINQ y otras extensiones de lenguaje capacitadas mediante LINQ es que toda la complejidad está oculta para los desarrolladores. No hace falta ver el listado número uno para utilizar la funcionalidad avanzada —sólo hay que escribir una consulta LINQ frente a ContinuousCollection en vez de un listado estándar y “simplemente funciona.”

Se pueden escribir muchas muestras diferentes para ilustrar cómo CLINQ se pone en acción, pero uno de los ejemplos más aplicables que he encontrado es un Libro de Pedidos. Una aplicación de libro de Pedidos controla los datos del mercado para un subconjunto seleccionado de símbolos. Sin CLINQ, tendríamos que establecer hilos de control manuales que estén pendientes de los cambios que se produzcan en los datos subyacentes del mercado, y después propagar dichos cambios de forma manual al modelo, que es después ligado a la GUI.

Ello crea muchas partes que se mueven, hace que la depuración sea difícil, y puede ser muy propenso al error. Con CLINQ, podemos establecer simplemente unas consultas en el código del controlador y todo se actualiza de forma dinámica según se necesita. Por ejemplo, en la aplicación muestra del libro de pedidos, para abrir un nuevo libro y crear una variable que contenga sólo el subconjunto de los datos del mercado perteneciente a un símbolo determinado, podemos usar una simple consulta CLINQ (una consulta LINQ frente a una ContinuousCollection). El Listado número 2 muestra el método de controlador SubscribeToSymbol, que crea una nueva ventana y establece las propiedades AskTicks y BidTicks de esa ventana en las consultas CLINQ.

//listado 2

public void SubscribeToSymbol(MarketSymbol symbol)

if (!ModelRoot.Current.SubscribedSymbols.Contains(symbol))

ModelRoot.Current.SubscribedSymbols.Add(symbol);

MarketDataBook newBook = new MarketDataBook(); // WPF Window

newBook.Symbol = symbol;

newBook.AskTicks =

from tick in ModelRoot.Current.MarketData

where tick.Side == TickSide.Ask &&

tick.Symbol == symbol

orderby tick.Price descending

select tick; // SELL

newBook.BidTicks =

from tick in ModelRoot.Current.MarketData

where tick.Side == TickSide.Bid &&

tick.Symbol == symbol

orderby tick.Price

select tick; // BUY

_monitorBooks.Add(symbol, newBook);


Una vez establecidas las propiedades AskTicks y BidTicks, esas colecciones se actualizarán de forma automática conforme van entrando más datos del mercado, sin requerir ningún esfuerzo más por parte del programador. Esta es una potente forma de abordar el procesamiento de mensajes y los datos del flujo.

Prestaciones

Nos resultó realmente interesante cuando estuvimos comprobando las prestaciones de CLINQ. Sin estar limitados a una GUI, podemos bombear más de 1.000.000 de cambios por segundo a una colección de fuentes (con un tope superior en el tamaño de la colección para evitar quedarnos sin recursos) con una carga grande de CPU, y a 100.000 cambios por segundo la carga parecía ser sostenible de forma indefinida.

La parte auténticamente problemática vino al intentar ligar CLINQ a una GUI WPF. Gracias a Pavan Podila de WPF Way (pavanpodila.spaces.live.com), pudimos descubrir un problema potencialmente fatal con el ligado de datos. En nuestro cuadro de listado, no se había establecido la altura de forma explícita, lo que impedía que el estilo de contenedor del elemento fuera un VirtualizingStackPanel.

Además había un selector de plantillas para seleccionar el estilo de rejilla alterna que se podría invocar en cada cambio para cada uno de los cientos de miles de filas ligadas. Lo aprendido: Hay que asegurarse de que nuestro control de ligado sigue siendo virtual y hay que tener cuidado con los estilos de elemento y los estilos de contenedor de elementos cuando se trabaja con grandes conjuntos de datos. Tomando esas precauciones, pudimos obtener casi el mismo rendimiento en CLINQ cuando estaba ligado a datos que cuando no usamos ninguna GUI.

Con un código como el del Listado número 2, he construido una demo muestra del Libro de Pedidos que nos permite abrir ventanas en símbolos arbitrarios con datos de mercado simulados que entran en intervalos fijos; véase la Figura 2.

Esta ventana contiene dos cuadros de listado. Cada cuadro de listado está ligado a un subconjunto de datos del mercado. El cuadro de listado BUY está ligado a los cambios en la oferta para AAPL y el cuadro de listado SELL está ligado a los cambios en la petición para AAPL. Según van entrando nuevos datos en el almacén central de datos del mercado (podría ser desde mensajes de red o, como es mi caso, desde un hilo de fondo en un temporizador), cada conjunto de resultados de consulta se actualiza de forma dinámica.

Si entran nuevos datos del mercado para otros símbolos que no sean AAPL, los resultados de la consulta no se ven afectados. Si se produce un nuevo movimiento para AAPL, se coloca en el cuadro de listado correcto, basándose en el lado del movimiento. Todo ese trabajo, toma de decisiones, y propagación de datos lo hace de forma automática la extensión de lenguaje CLINQ.

((Cada cuadro de listado está ligado a un subconjunto de datos del mercado. El cuadro de listado BUY está ligado a los cambios en la oferta para AAPL y el cuadro de listado SELL está ligado a los cambios en la petición))

Afortunadamente, las extensiones de lenguaje y específicamente CLINQ pueden utilizarse para el placer al igual que para los negocios. La consulta siguiente es una que podría usarse en un juego de estrategia para detectar todos los buques cercanos dentro del alcance concreto de nuestro radar:


_visibleObjects =

from radarObject in _rawRadar

where radarObject

.Distance2DFrom(_myLocation)

<= RADAR_RADIUS select radarObject;

Este juego podría recibir un flujo continuo de mensajes de red desde aplicaciones conectadas de igual nivel y almacenar dichos mensajes en una ContinuousCollection (como _rawRadar). La colección _visibleObjects se actualizaría de forma automática cada vez que un objeto en el listado _rawRadar cambiara de posición u se añadieran o eliminaran objetos.

La Figura 3 muestra los resultados de esa consulta ligada a un cuadro de diálogo de estilo personalizado para crear una vista de radar basada en WPF de los enemigos cercanos.

Conclusión

LINQ Continuo no es más que una expansión hecha posible mediante extensiones de lenguaje y LINQ en la Plataforma 3.5 de .NET. Al adoptar las características de la próxima versión de .NET, estaremos preparados no sólo para ofrecer las características más avanzadas en nuestras aplicaciones, sino que también tendremos la garantía de que si hay características de consulta específicas de dominio que deseamos tener en nuestra aplicación, las podremos crear nosotros mismos de forma rápida y fácil.

Se puede descargar las muestras utilizadas en el presente artículo, además del mismo código CLINQ, desde mi blog en dotnetaddict.dotnetdevelopersjournal.com. DDJ

Kevin Hoffman es desarrollador de Investigaciones en Liquidnet, sistema que opera en acciones, en donde investiga sobre Web 2.0, el software social, y .NET 3.0. Se puede contactar con él en alothien@gmail.com

Etiquetas

Noticias relacionadas

Comentarios

No hay comentarios.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Debes haber iniciado sesión para comentar una noticia.