MSDN Blogs
  • WarNov Developer Evangelist

    LINQ: Consultando tablas cruzadas en Visual Studio LightSwitch

     

    LightSwitch es un RAD para hacer aplicaciones basadas en datos bien sea usando Silverlight, o HTML5. En cuestión de minutos podemos tener una aplicación de ese estilo desplegada y funcionando.

    Cuando ya estamos desarrollando una app de cierta complejidad sin embargo, tarde o temprano llegamos a la necesidad de consultar data que se encuentra distribuida en varias tablas en un ambiente relacional.

    Aunque LightSwitch provee un wizard de consultas básicas, éstas últimas requieren la escritura de código.

    El código usado para escribir consultas complejas el LightSwitch no es más que LINQ.

    En mi primer intento de hacer una consulta cruzada con LINQ dentro de LightSwitch sin embargo, a pesar de escribir instrucciones completamente válidas de acuerdo a la definición de LINQ, siempre obtenía un error emitido por LightSwitch, que además dado el mensaje presentado, no da muchas luces de qué es lo que está sucediendo:

    Unable to create a constant value of type ‘LightSwitchApplication.MiTabla’. Only primitive types or enumeration types are supported in this context.

    Pasé mucho tiempo experimentando qué podría ser hasta que descubrí que el error sucede cuando en el where de la instrucción LINQ ponemos dos tablas de la DB directamente. Entonces una solución que se me ocurrió y que mi buen amigo @jramirezdev me ayudó a implementar fue descargar primero unos datos, enumerarlos en una lista y luego con joins lograr el resultado. Sin embargo, dado que esto requiere dos llamados distintos a la DB y además resulta en una consulta LINQ difícil de leer, seguí “escarbando” y encontré una gran ayuda en el operador any  de LINQ, que me permite seleccionar los datos que quiero sin tener que citar varias tablas dentro de LINQ, sino más bien explorando las propiedades de navegación del modelo de objetos.

    En este artículo, he invitado precisamente a @jramirezdev, a quien agradezco el haber escrito el proceso de problema y solución en detalle; algo muy valioso para desarrolladores que estén teniendo este mismo problema:

    ...

    Es muy común que en nuestras aplicaciones tengamos un origen de datos relacional y sobre este debamos realizar consultas en varias tablas para obtener la información que necesitamos. En LightSwitch las consultas simples se puede realizar con el diseñador integrado, pero si este no es el caso debemos recurrir a crear una consulta desde ceros, esto es, usar EntityFramework + LINQ.

    Cuando utilizamos Entity Framework en LightSwitch nos encontraremos con algunas limitaciones, es por esto que debemos ser cuidadosos pues aunque la consulta LINQ compile no significa que al ejecutarla vayamos a obtener los resultados esperados.

    Para entender esto veremos un caso específico donde realizaremos una consulta de tablas cruzadas con LINQ.

    Aplicación: Crearemos una aplicación que permitirá registrar Usuarios, Tiendas y Facturas. Para esto debemos tener en cuenta que una factura será específica de un Usuario y una Tienda, esto significa que debe existir una relación entre Usuario y Tienda para finalmente registrar las facturas de ese usuario en esa tienda.

    Modelo Relacional:

    Problema: Se necesita generar un listado de todas las facturas que existan en las tiendas donde está registrado el usuario "Jorge Ramirez".

    Para resolver este problema comenzaremos por crear una solución de LS, que se vera de la siguiente manera:

    Aquí podemos observar que debajo de la tabla "Facturas" encontramos una consulta llamada "GetFacturasPorTiendasAsociadasAlCliente", es en ese archivo que escribiremos nuestra consulta LINQ.

    Escribiré mi primer consulta de la siguiente manera.

            partial void 
    GetFacturasPorTiendasAsociadasAlCliente_PreprocessQuery
    (
    ref IQueryable<Factura> query) { //Consulta 1 var tiendas = from UsuarioTienda ut in UsuarioTiendas where ut.Usuario.Nombre == "Jorge Ramirez" select ut; var facturas = from Factura factura in query from UsuarioTienda ut in tiendas where factura.UsuarioTiendas.Tienda.Id ==
    ut.Tienda.Id select factura; query = facturas; }
    Semánticamente esta consulta es correcta, pero cuando la ejecutamos obtenemos el siguiente error:

    Resultado Consulta 1: Error.

    Este error aunque pareciera claro, no lo es, pues fácilmente nos confunde el pensar ¿Dónde creamos un Obj del tipo "UsuarioTienda"? Pues bueno, aquí es importante conocer el concepto de que son Propiedades de Navegación de EntityFramework.

    Leyendo este error vemos que existe una limitación para acceder a las tablas relacionadas/cruzadas a través de las propiedades de navegación, lo cual nos indica que debemos considera una solución más del lado semántico de TSQL donde hacemos los JOINS de forma explícita.

           partial void 
    GetFacturasPorTiendasAsociadasAlCliente_PreprocessQuery
    (
    ref IQueryable<Factura> query) { //Consulta 2 var facturas = (from UsuarioTienda utienda in UsuarioTiendas join Usuario usuario in Usuarios on
    utienda.Usuario.Id equals usuario.Id join Tienda tienda in Tiendas on
    utienda.Tienda.Id equals tienda.Id join Factura factura in query on tienda.Id
    equals factura.UsuarioTiendas.Tienda.Id where usuario.Nombre == "Jorge Ramirez" select factura).AsQueryable(); query = facturas; }

    Resultado Consulta 2: Exito.

    De esta forma EntityFramework ha logrado resolver las relaciones y entregarnos la data que necesitamos.

    También podemos considerar otra posibilidad jugando con las propiedades de navegación y una expresión Lambda para que nuestra consulta sea más corta.

            partial void 
    GetFacturasPorTiendasAsociadasAlCliente_PreprocessQuery
    (
    ref IQueryable<Factura> query) { //Consulta 3 var facturas = from Factura factura in query where factura.UsuarioTiendas
    .Tienda.UsuarioTiendas.
    Any(c => c.Usuario.Nombre == "Jorge Ramirez") select factura; query = facturas; }

    Resultado Consulta 3: Exito.

    PROFILING

    Finalmente como un proceso educativo, realizaremos uso de un perfilador (profiler) para ver como se comportan nuestras dos consultas exitosas.

    Consulta 2: Reporte

    Consula 2: Entity SQL Generado

    SELECT TOP (45) [Project1].[Id]     
    AS [Id], [Project1].[RowVersion]
    AS [RowVersion], [Project1].[Producto]
    AS [Producto], [Project1].[Valor]
    AS [Valor], [Project1].[UsuarioTienda_Factura]
    AS [UsuarioTienda_Factura], [Project1].[C1]
    AS [C1], [Project1].[C2]
    AS [C2], [Project1].[Id1]
    AS [Id1], [Project1].[RowVersion1]
    AS [RowVersion1], [Project1].[Tienda_UsuarioTienda]
    AS [Tienda_UsuarioTienda], [Project1].[UsuarioTienda_Usuario]
    AS [UsuarioTienda_Usuario] FROM (SELECT [Project1].[Id]
    AS [Id], [Project1].[RowVersion]
    AS [RowVersion], [Project1].[Producto]
    AS [Producto], [Project1].[Valor]
    AS [Valor], [Project1].[UsuarioTienda_Factura]
    AS [UsuarioTienda_Factura], [Project1].[Id1]
    AS [Id1], [Project1].[RowVersion1]
    AS [RowVersion1], [Project1].[Tienda_UsuarioTienda]
    AS [Tienda_UsuarioTienda], [Project1].[UsuarioTienda_Usuario]
    AS [UsuarioTienda_Usuario], [Project1].[C1]
    AS [C1], [Project1].[C2]
    AS [C2], row_number() OVER (ORDER BY [Project1].[Id] ASC)
    AS [row_number] FROM (SELECT [Extent1].[Id]
    AS [Id], [Extent1].[RowVersion]
    AS [RowVersion], [Extent1].[Producto]
    AS [Producto], [Extent1].[Valor]
    AS [Valor], [Extent1].[UsuarioTienda_Factura]
    AS [UsuarioTienda_Factura], [Extent2].[Id]
    AS [Id1], [Extent2].[RowVersion]
    AS [RowVersion1], [Extent2].[Tienda_UsuarioTienda]
    AS [Tienda_UsuarioTienda], [Extent2].[UsuarioTienda_Usuario]
    AS [UsuarioTienda_Usuario], N'UsuarioTiendas'
    AS [C1], N'UsuarioTiendas'
    AS [C2] FROM [dbo].[Facturas] AS [Extent1] INNER JOIN [dbo].[UsuarioTiendas]
    AS [Extent2] ON [Extent1].[UsuarioTienda_Factura] =
    [Extent2].[Id]) AS [Project1]) AS [Project1] WHERE [Project1].[row_number] > 0 ORDER BY [Project1].[Id] ASC

    Consulta 3: Reporte

    Consulta 3: Entity SQL Generado

    SELECT TOP (45) [Project2].[Id]
    AS [Id], [Project2].[RowVersion]
    AS [RowVersion], [Project2].[Producto]
    AS [Producto], [Project2].[Valor]
    AS [Valor], [Project2].[UsuarioTienda_Factura]
    AS [UsuarioTienda_Factura], [Project2].[C1]
    AS [C1], [Project2].[C2]
    AS [C2], [Project2].[Id1]
    AS [Id1], [Project2].[RowVersion1]
    AS [RowVersion1], [Project2].[Tienda_UsuarioTienda]
    AS [Tienda_UsuarioTienda], [Project2].[UsuarioTienda_Usuario]
    AS [UsuarioTienda_Usuario] FROM (SELECT [Project2].[Id]
    AS [Id], [Project2].[RowVersion]
    AS [RowVersion], [Project2].[Producto]
    AS [Producto], [Project2].[Valor]
    AS [Valor], [Project2].[UsuarioTienda_Factura]
    AS [UsuarioTienda_Factura], [Project2].[Id1]
    AS [Id1], [Project2].[RowVersion1]
    AS [RowVersion1], [Project2].[Tienda_UsuarioTienda]
    AS [Tienda_UsuarioTienda], [Project2].[UsuarioTienda_Usuario]
    AS [UsuarioTienda_Usuario], [Project2].[C1]
    AS [C1], [Project2].[C2]
    AS [C2], row_number() OVER
    (ORDER BY [Project2].[Id] ASC) AS [row_number] FROM (SELECT [Extent1].[Id]
    AS [Id], [Extent1].[RowVersion]
    AS [RowVersion], [Extent1].[Producto]
    AS [Producto], [Extent1].[Valor]
    AS [Valor], [Extent1].[UsuarioTienda_Factura]
    AS [UsuarioTienda_Factura], [Extent2].[Id]
    AS [Id1], [Extent2].[RowVersion]
    AS [RowVersion1], [Extent2].[Tienda_UsuarioTienda]
    AS [Tienda_UsuarioTienda], [Extent2].[UsuarioTienda_Usuario]
    AS [UsuarioTienda_Usuario], N'UsuarioTiendas'
    |
    AS [C1], N'UsuarioTiendas'
    AS [C2] FROM [dbo].[Facturas] AS [Extent1] INNER JOIN [dbo].[UsuarioTiendas]
    AS [Extent2] ON [Extent1].[UsuarioTienda_Factura] = [Extent2].[Id] WHERE EXISTS (SELECT 1 AS [C1] FROM [dbo].[UsuarioTiendas] AS [Extent3] INNER JOIN [dbo].[UsuarioTiendas] AS [Extent4] ON [Extent3].[Tienda_UsuarioTienda] =
    [Extent4].[Tienda_UsuarioTienda] INNER JOIN [dbo].[Usuarios] AS [Extent5] ON [Extent4].[UsuarioTienda_Usuario] =
    [Extent5].[Id] WHERE ([Extent1].[UsuarioTienda_Factura] =
    [Extent3].[Id]) AND (N'Jorge Ramirez' = [Extent5].[Nombre])))
    AS [Project2]) AS [Project2] WHERE [Project2].[row_number] > 0 ORDER BY [Project2].[Id] ASC

    Nota: El perfilador usado fue Entity Framework Profiler, podemos aprender como usarlo Aquí.

    Con esto hemos finalizado el proceso de creación y análisis de consultas LINQ to Entities en LightSwitch.

    Muchas Gracias.


    Jorge Ramírez (@JramirezDev): Es Tecnólogo de Sistemas y estudiante de Ingeniería de Sistemas, con 4 años de experiencia en desarrollo de BackEnd y FrontEnd usando .NET Framework; especialista en LINQ y WCF.  Encuentren la versión original de este post y mucho contenido adicional muy interesante en su blog.




  • WarNov Developer Evangelist

    Flash: Verdades en Windows 8

    A partir de hoy, Flash es soportado en Windows 8 nuevamente.

    Qué?

    Alguna vez dejó de ser soportado?

    Antes de responder esta pregunta, déjenme contarles acerca de los sabores de Internet Explorer 10 en Windows 8.

    Básicamente son 4 sabores:

    2 sabores para Windows 8 sobre arquitecturas x86 y x64 (llamémoslas tradicionales) y otros 2 sabores para WindowsRT. Entonces, en las arquitecturas tradicionales los sabores de IE que tenemos son IE de escritorio y el IE de interfaz moderna o inmersiva.

    Lo mismo pasa en WindowsRT. Recuerden que en WindowsRT también hay un desktop, solo que ese desktop no soporta la instalación de nuevas aplicaciones. Las apps a instalarse solo pueden ser aquellas de la interfaz moderna o Windows Store. Entonces en este caso también tenemos 2 versiones de Internet Explorer. Una de desktop y otra de interfaz moderna.

    Cuando se lanzó Windows 8 el soporte a Flash se limitó de la siguiente manera:

    1. WindowsRT sólo permitía visualizar ciertos sitios web incluidos en una lista blanca, con Flash en ambos sabores del browser.
    2. Windows 8 sólo permitía visualizar ciertos sitios web incluidos en una lista blanca, con Flash en la interfaz inmersiva o moderna.
    3. Windows 8 tenía soporte full para visualizar todos los sitios web con Flash.

    flashpluswin8

     

    Por qué la limitante?

    Pensemos por un momento en la filosofía de Apps: Proveer un mercado de aplicaciones confiables del que solo se pueden bajar apps seguras que no vayan a hacerle ningún daño al usuario. Incluyendo ausencia de virus, spyware, troyanos y demás. Para poder lograr esto, las apps deben correr en un entorno seguro o sandbox que llaman. Del que no pueden salirse para ir a atacar o controlar otras apps. Además de esto, la filosofía de apps también incluye desarrollo amigable con la batería.

    Entonces lo que sucede con Flash es que algunos sitios no respetaban ese sandbox y representaban huecos de seguridad para el sistema. Así que uno piensa: para que un entorno tan protegido como el sandbox si los atacantes pueden entrar a través de flash?

    Además otros sitios con sus apps de flash exigían un esfuerzo enorme a los procesadores central y gráfico, lo que obviamente gastaba la batería en muy corto tiempo. Esto también derivaría en una pobre experiencia de usuario.

    En conclusión la limitante se impuso para proteger la experiencia de usuario. No con el ánimo de excluir a un jugador importante de la industria sin el cual por ejemplo, mucho del contenido con DRM que está en la web no podría ser presentado (recordemos que HTML5 aún no define un estándar para manejar esta tecnología. Es algo que por el momento debe definir cada códec, de manera que la tarea se hace complicada a la hora de manejarlo.)

    Lista blanca?

    Sí, no todos los sitios que usan flash tenían esos problemas de seguridad y performance. Así que en el pasado uno podía pedirle a Microsoft que evaluara su sitio y permitiese a Flash ejecutar el contenido.

    Cuál es entonces el soporte que se brinda hoy en día

    Básicamente se cambió de tener una lista blanca a una lista negra. Ahora por defecto todos los sitios con flash pueden visualizarse en cualquiera de los 4 sabores de browser descritos anteriormente, a menos de que el sitio esté plenamente identificado como inseguro o de pobre performance y por ende se haya agregado a una lista negra.

    TODOS los sitios con Flash que se pueden ver hoy en día en el IE desktop sobre Windows 8 (x86 y x64), se podrán seguir viendo sin ningún problema.

    Cómo se logró esto?

    Gracias a un trabajo conjunto entre Microsoft y Adobe, se logró adicionar a Windows 8 y Windows RT una nueva versión de Adobe Flash totalmente optimizada para el uso Inmersivo o de interfaz moderna. Esta versión es mucho más generosa con la batería, con el performance y con la seguridad del sistema y demuestra totalmente el compromiso que Microsoft tiene con los usuarios de ofrecerles la mejor experiencia, independientemente de que esto implique incluir productos de otros fabricantes.

    A partir de cuándo lo puedo tener?

    Como es tradicional ya con Windows 8, todos los martes podemos tenemos actualizaciones. En la de hoy, martes 12 de marzo, estaremos incluyendo esta actualización entre otras muy importantes. Por ejemplo estaremos actualizando el firmware de la Surface para corregir algunos problemas menores.

     

    image 

  • WarNov Developer Evangelist

    WebAPI, Azure y Apps: Una implementación

    Esta presentación la he usado en varios eventos últimamente, como el TechDay de Cali, Medellín y algunas charlas online. Nos muestra unos temas que ya he tratado en varios de mis posts anteriormente, relacionados con Windows Azure Mobile Services, Windows Azure Storage, Web Api y Apps.

    Aquí quise integrar todos esos posts en un ejemplo de la vida real, al que le damos solución de una manera bastante sencilla:

    Cómo compartir archivos desde un dispositivo móvil a otros, a través de una Plataforma propia.

    Toda la teoría relativa a esta demostración la puedes encontrar aquí.

    Veamos pues la presentación en video:

  • WarNov Developer Evangelist

    Noding w/ @WarNov. Episodio 2: JavaScript para Orientados a Objetos

    Luego de la gran acogida del episodio de lanzamiento del curso de NodeJS que publiqué hace unos días, me permito presentarles en esta ocasión el segundo capítulo de la serie. Saliendo un poco más tarde de lo planeado debido a que lo que inicialmente concebí como un pequeño tutorial de  JavaScript, terminó siendo un curso completo de 1 hora.

    Esto me exigió repasar nuevamente toda la teoría (Una semana de lectura) y luego condensar todo esto en la menor cantidad de tiempo en video que pude durante un proceso de edición de 8 horas.

    Finalmente, aquí está el resultado:

    image

    Un recorrido súper rápido por JavaScript. Como ustedes sabrán del primer capítulo, es el lenguaje protagonista en NodeJS. Así que en este episodio encontrarán los aspectos fundamentales del lenguaje ha tener en cuenta.

    El episodio está enfocado a desarrolladores orientados a objetos en lenguajes tradicionales (De allí el título del mismo). De manera que si eres experto en C#, C++ o VB pero no has estado en contacto muy profundo con JavaScript, este curso será excelente no solo para que comiences luego con Node.JS sino también con Apps para Windows 8 en JavaScript + HTML5, HTML5 en el browser, JQuery, LIGHTSWITCH basado en HTML5 y hasta Napa: el conjunto de tools para hacer apps basadas en Office. Así pues, es un episodio que te abrirá las puertas a muchas otras tecnologías.

    Este es el segundo episodio de Noding w/ @WarNov. El curso rápido de Node.js que te permitirá de manera rápida y fácil aprender de qué se trata Node.js y cómo utilizarlo.

    Este episodio es un recorrido rápido por JavaScript para developers Orientados a Objetos (aquellos con experiencia en lenguajes como C++, C# o VB), que aunque han tenido contacto con JavaScript no tienen los conocimientos avanzados de ese lenguaje para poder incursionar en Node.js. Ya vimos en el episodio anterior que JavaScript es central en Node.js. Por esto es necesario tener conocimientos sólidos del lenguaje antes de arrancar con Node.js.

    Adicionalmente, en este video muestro como integrar Visual Studio con Node.js a través de la consola de comandos de NuGet Package Manager.

    Si ya eres experto en JavaScript y deseas pasar de inmediato a NodeJS, puedes hacer skip de este episodio y continuar con el siguiente.

    Sin más preámbulos, aquí los dejo con Noding w/ @WarNov. Episodio 2: JavaScript para Orientados a Objetos.

Page 1 of 1 (4 items)