[LINQ] Maestro – Detalle en un mismo Registro (2)

Ayer os hablaba de la necesidad de mostrar en una misma línea los datos de Cabecera y Detalle asociados.

Para solventarlo estuve probando con una clase y con una consulta de LINQ, ya que no podía probarlo el proyecto de EF donde quería aplicarlo.

Cuando me disponía a implementarlo en el proyecto bueno, me encontré con el siguiente mensaje de error:

LINQ to Entities no reconoce el método ‘System.String Join(System.String, System.Collections.Generic.IEnumerable`1[System.String])’ del método, y este método no se puede traducir en una expresión de almacén.

La consulta que ha provocado este error ha sido:

   1: var consulta = (from TCabecera cab in 

   2:                  contexto_local.TCabecera  

   3:    let Detalle = (from TDetalle detalle 

   4:                    in cab.TDetalle 

   5:    select detalle.Nombre)    

   6:    let Nombres = string.Join(",",Detalle )     

   7:    select new   

   8:       {  

   9:        cab.Campo1, 

  10:        cab.Campo2, 

  11:        Nombres

  12:        }).ToList();

  13: grid.DataSource=consulta;

 

¿Por qué se produce este error?

El error se produce al intentar asignar a Nombres la unión de Detalle. Este se debe a que no se puede convertir en un primer momento, la consulta a TSQL.

¿Cómo Solucionarlo?

Para poder solucionarlo vamos a necesitar ejecutar la consulta en 2 pasos:

   1: var consulta = (from TCabecera cab in   

   2:                 contexto_local.TCabecera    

   3:       let Detalle = (from TDetalle detalle   

   4:                      in cab.TDetalle   

   5:                      select detalle.Nombre)      

   6:      select new     

   7:       {    

   8:         cab.Campo1, 

   9:         cab.Campo2,  

  10:         Detalle 

  11:         }).ToList();  

  12: var consulta2 = (from dato in consulta

  13:             let Nombes = string.Join(",",dato.Detalle)

  14:             select new 

  15:             {

  16:             dato.Campo1,

  17:             dato.Campo2,

  18:             Nombres

  19:             };

  20:         grid.DataSource=consulta2.ToList();

CURIOSIDAD

Esto me ha sucedido al hacer la consulta contra Entidades de EF (LINQ-To-Entities). Decidí probarlo contra LINQ-to-SQL y cual fue mi sorpresa:

Funcionó en un único Paso!

Saludos, y espero que os sea de utilidad.

[LINQ] Maestro – Detalle en un mismo Registro (1)

Problema

Lo primero, voy a exponer el problema que me han planteado:

Dada una tabla, digamos, “Cabecera”, con una tabla relacionada de “Detalle” con una relación 1 a N. Necesitamos mostrar todos los datos del detalle, asociados a una fila de la cabecera sin repetir los datos de la cabecera.

Digamos que la representación Sería algo así:

Cabecera
C1 C2 C3
1 P1 01/01/2011
2 P2 01/02/2011
Detalle
1 1 D1
2 1 D2
3 1 D3
4 2 D1
5 2 D4

 

RESULTADO A OBTENER
1 P1 01/01/2011 D1, D2, D3
2 P2 01/02/2011 D1,D4

 

Enfoques

Así a bote pronto, se me ocurren 3 posibles enfoques:

  1. Hacerlo desde la BD.
  2. Hacerlo desde Código .NET con bucles…
  3. Hacerlo con LINQ

¿Hacerlo desde la BD?

La primera opción que se me ha ocurrido (por eso de que me gustan bastante las BD), ha sido: Esto se podría resolver creando un SP, Función y a través de un cursor devolver el dato separado por el carácter indicado.

Es una opción que he descartado porque no siempre tenemos la opción de modificar la BD a nuestro gusto.

¿Hacerlo desde Código .NET?

La siguiente alternativa sería, obtener los datos desde la BD con una consulta “JOIN” entre las tablas de Cabecera y Detalle, recorrer las filas, y generar un “nuevo DataTable”…

Descartada….

¿Hacerlo con  LINQ?

La opción de LINQ, nos va a permitir que no solamente lo podamos hacer contra la BD (ya sea con EF o con LINQTOSQL), sino también contra Objetos o XML…

Para demostrarlo voy a mostrar un pequeño Ejemplo con una Clase y Listas.

EJEMPLO

Dada una clase Empleado:

   1: public class Empleado

   2: {

   3:     public Empleado() { }

   4:     public Int32 ID { get; set; }

   5:     public String Nombre { get; set; }

   6:     public String Apellido { get; set; }

   7:     public List<string> Numeros { get; set; }

   8: }

La clase Empleado se va a corresponder con la tabla “Cabecera” y la lista Números  se va a corresponder con la tabla “Detalles”.

A continuación, para obtener el resultado indicado arriba, voy a realizarlo a través de una consulta LINQ:

   1: List<Empleado> listado = new List<Empleado>();

   2: //Rellenar Listado

   3: var consulta = from Empleado em in listado

   4:     let Numeros = string.Join(";", em.Numeros)

   5:     select new { 

   6:     em.Nombre,

   7:     Numeros

   8:     };

En la consulta estoy empleado el Operador LET, el cual va a permitir “almacenar” una expresión, en este caso el resultado de la función Join de la clase String, la cual se encarga de unir, un conjunto de datos, separándolos por el carácter indicado.

Os dejo el enlace al proyecto de ejemplo que he utilizado para que lo comprobéis con vuestros ojos. 😀

 

Saludos

Problemas con Oracle ODP.NET para EF

Tengo la suerte o desgracia (después de más de 3 años trabajando contra Oracle, todavía no me decanto), de trabajar contra ORACLE.

Este fin de semana he visto publicado en el Blog de Unai Zorrilla, que Oracle había anunciado una beta para poder conectarse a Oracle con Entity Framework.

Bueno, pese a ser una Beta, he decidido descargarlo y probarlo con VS2010, para ver que tal funciona, y de esa manera intentar empezar a usar EF en nuevos proyectos contra ORACLE si me dejan.

El primer problema que me he encontrado, ha sido muy rápido, a la hora de instalar ODAC. A la hora de definir las rutas de instalación se observan 2 cajas de texto, y una tercera se observa ligeramente, pero cual es mi sorpresa, "No hay Scroll” por lo que la he dejado con el valor que tenía…..

Bueno, finalizada la instalación, configuro los ficheros SQLNET.ORA y TNSNAMES.ORA para definir mis BD’s.

Una vez configurados, arranco VS2010, y me digo, “Vamos a probar si el driver conecta correctamente”.

Para ello, presiono Ctrl + Alt + S, lo que hace que aparezca el Explorador de Servidores de VS. Agrego una nueva conexión, selecciono “Oracle Provider for .NET”. selecciono mi cadena de conexión, establezco usuario y contraseña, y le doy a “Test Connection”. Conexión perfecta. En el explorador de Servidores puedo ver todos los objetos de mi BD: Tablas, Vistas, Procedimientos….

Digo, que bien, y funciona muy bien el explorador ver los datos y tal…

Bueno vamos a probar con un proyecto y con EF. Creo un nuevo proyecto. Presiono “Agregar Nuevo Objeto” Selecciono “Entity Framework Data Model”, indico la Base de Datos, y voy al paso siguiente donde vamos a elegir los objetos de la BD que vamos a utilizar, donde me encuentro el problema nº 2.

Esto provoca que no pueda seguir probando!!!

Por lo que, tras un rato buscando información de manera poco satisfactoria decidí publicar un mensaje en el foro de Oracle, a ver si el equipo de desarrollo puede darme más detalles para poder seguir probando.

Continuara…