LINQ. Enigma recursivo

Lo tengo que reconocer, soy un viciado de las consultas SQL. Me recuerdan a los problemas de lógica en los que tenías que averiguar quién hace qué y cómo, y resolver un enigma.

logic_tapalogicextra_tapa

Pues así veo las consultas SQL, como un enigma a resolver. Me he pasado horas dándole vueltas a las consultas para hacer joins imposibles y subconsultas buscando obtener los datos en una sola llamada y con el mejor rendimiento posible. La verdad es que, a veces, echo de menos esas consultas de varias páginas tiradas en el Administrador de consultas del SQL Server 6.5 o 2000.

70-228-6-5

Con el tiempo vas evolucionando y te vas pasando a los ORM (Hibernate, LINQ to SQL, Entity Framework, LLBLGEN), y dejas de lanzar esas consultas y simplificas la lógica para que se adapten a las entidades, aún así seguías queriendo meter código en procedimientos almacenados para no olvidar aquella parte de ti, que controlas y que te gusta.

Desde que nació LINQ me pareció el gran avance de la programación actual. Cuando lo empiezas a usar, te das cuenta que vas a dejar de echar de menos a aquellas consultas y empiezas a olvidarte de los procedimiento almacenados. Pasas a otro nivel.

En ese nivel, empiezas a lanzar consultas sobre listas, enumerados y colecciones (y a todo lo que te echen por delante, que si no es consultable buscas la forma de que lo sea), y empieza a aparecer, de nuevo, los enigmas de aquellas consultas SQL, ahora en C#.

No lo puedo evitar y busco siempre la mejor consulta y la más eficiente, incluso me pongo a calcular el rendimiento de hacerla con LINQ y sin LINQ.

El último reto fue hacer una búsqueda recursiva, a través de listas de elementos enlazados con listas de esos elementos, utilizando LINQ.  Partimos de la siguiente clase que contiene una lista de elementos hijos de su misma clase.

public class Organizacion

{

    public Guid ID { get; set; }

    public OrganizationProfile Profile { get; set; }

    public string DisplayName { get; set; }

    public Organizacion Parent { get; set; }

    public List<Organizacion> Childs { get; set; }

    public UserProfile Leader { get; set; }

    public List<UserProfile> Members { get; set; }

}

Necesitábamos realizar una búsqueda recurrente en la lista principal que a su vez hiciera la misma búsqueda en los hijos (List<Organizacion> Childs) que a su vez buscara en los hijos de los hijos de los hijos… y así sucesivamente.

Este problema lo solucionaríamos con una búsqueda recursiva que fuera recorriendo todos los elementos, bajando y realizando la búsqueda en cada lista de hijos que fuera encontrando.

Cómo no me puedo estar quieto, me puse a buscar una solución que me permitiera hacer esa búsqueda en una consulta LINQ y ya llegué a la solución de crearme un método extensor que utiliza el combinador Y para realizar lambdas recursivas. Este método quedó tal como sigue:

private delegate Func<A, R> Recursive<A, R>(Recursive<A, R> r);

private static Func<A, R> Y<A, R>(Func<Func<A, R>, Func<A, R>> f)

{

    Recursive<A, R> rec = r => a => f(r(r))(a); return rec(rec);

}

 

public static IEnumerable<Organizacion> Recorrer(this IEnumerable<Organizacion> source, Func<Organizacion, bool> function)

{

    var traverse = Extensions.Y<IEnumerable<Organizacion>, IEnumerable<Organizacion>>(

        f => items =>

        {

            var r = new List<Organizacion>(items.Where(function));

            r.AddRange(items.SelectMany(i => f(i.Childs)));

            return r;

        });

 

    return traverse(source);

}

Nos creamos un delegado de la función recursiva (Y) y la función que recorre todos los elementos navegando por la lista de Childs y devolviendo un enumerable de todos los elementos de todas las listas.

Con lo que es muy sencillo y limpio (además de bonito y satisfactorio Sonrisa ) hacer la consulta para encontrar un elemento de todas las colecciones, utilizando el método extensor.

 

Organizacion parentOrg = OrganizacionList.Recorrer(o => o.DisplayName == manager["Department"].ToString()).FirstOrDefault();

 

Lo sé, debería de pasar más tiempo haciendo otras cosas más útiles, pero, y lo bien que me lo he pasado….

 

Saludos a todos…

2 comentarios sobre “LINQ. Enigma recursivo”

  1. Excelente aportación!!!!

    Pero una duda o aclaración. Existen muchas formas o alternativas para resolver las casuísticas o casos de uso que nos plantean los usuarios, pero hablando técnicamente, si me encuentro en un escenario dónde los desarrollos a realizar siempre se hacen sobre SQL Server, dónde en la medida de lo posible tratas de tener una capa de negocio independiente de la capa de presentación, digo en la medida de lo posible porque no siempre hay tiempo para hacer un buen desarrollo y a veces te ves obligado a tomar atajos, porque el negocio es lo primero.
    Es tan flexible y eficiente el LINQ para poder desprenderte por completo de la confección de tus procedimientos??? En el momento que haces tus análisis de uso del motor SQL y necesitas hacer ajustes, no es más fácil y sencillo hacerlos en los procedimientos del SQL a tener que tocar y hacer cambios en la programación de la capa de negocio??
    Desde mi punto de vista pienso que es más flexible mantener las declaraciones de integridad, formas y métodos de accesos a la información en procedimientos del SQL (entidades básicas de la información) en el SQL Server y luego en las capas asociadas al negocio hacer las llamadas a estos procedimientos a través de la tecnología de programación sea LINQ u otras alternativas y por último la capa de presentación Silverlight, aplicaciones Windows, aplicaciones móviles.

    Tú que opinas???

  2. Gracias Frank.

    ¿cuándo cambias una consulta en un procedimiento almacenado te obliga a cambiar tu código para contemplar un campo nuevo? Esto también lo tendrás que hacer en LINQ, volver a compilar tu aplicación para que reconozca ese campo nuevo.

    Si hablamos de LINQ to SQL, pensemos en una herramienta que ayuda a desarrollar más rápido y eficientemente aplicaciones con SQL Server, sea utilizando LINQ para consultas o utilizando los procedimiento almacenados que tengas en tu base de datos.

    Sin embargo, si hablamos de Entity Framework, tenemos la misma flexibilidad, o incluso mayor flexibilidad, con los procedimiento almacenados, pero no es el modo ideal de trabajo para un ORM. Piensa que la aportación de un ORM es la capacidad de poder realizar consultas sobre las entidades (objetos) y NO tener dependencia sobre tu base de datos.

    A mi, personalmente, no me gusta tener la lógica de negocio de mis aplicaciones en el SQL Server, aunque si que reconozco que ciertas consultas son necesarias en procedimiento almacenados porque mejora el rendimiento. Lo normal en mis desarrollos es trabajar con cuatro capas (interfaz de usuario, lógica de negocio, acceso a datos y almacenamiento de los datos).

Deja un comentario

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