none
Select en Consulta con LinQ Dynamic RRS feed

  • Pregunta

  • Estoy intentando hacer una consulta con LinQ Dynamic ya que el Select de la consulta varía en tiempo de ejecución.

    En un principio, pensaba que mi necesidad se podía resolver de otro modo pero, tras varias consultas en este foro, al final he llegado a la conclusión de que lo más apropiado es usar LinQ Dynamic.

    En primer lugar habría que declarar y definir la variable que va a armar el Select de la consulta LinQ. Sería algo así pero así no es porque no funciona. Quisiera saber cual es la forma correcta de definirla.

                    var miSelect = DynamicExpressionParser.ParseLambda("Id = d.Id, Titulo = d.Titulo, Autor = d.Autor, Editorial = d.Editorial");

    Haciendo esto, Visual Studio me dice que ParseLambda no es correcto. Dice que "Ninguna sobrecarga para el método ParseLambda toma 1 argumentos"

    Una vez definida esta variable, la pasaríamos en el Select de la consulta LinQ, pero también de forma correcta.

                    Libros miConsulta = (from d in db.Libros

                                         where d.Id == id
                                         select new Libros { miSelect }).FirstOrDefault();

    Espero haberme explicado bien y agradezco a quién me pueda echar una mano con esto que llevo varios días dándole vueltas y no avanzo.

    Muchas gracias.

    sábado, 2 de mayo de 2020 0:43

Respuestas

  • Debería ser así tu código

    var miSelect = DynamicExpressionParser
                   .ParseLambda(typeof(MyModel),
                                null, 
                                "Id = d.Id, Titulo = d.Titulo, Autor = d.Autor, Editorial = d.Editorial");  
    



    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta anespri miércoles, 6 de mayo de 2020 16:28
    domingo, 3 de mayo de 2020 23:23
    Moderador
  • Hola, he hecho pruebas y no hace falta hacer lo que estabas haciendo. Te pongo un ejemplo que me funciona en un app de consola

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Dynamic.Core;
    
    namespace proyhilobanco
    {
        public static class Program
        {
            
            public class Libros
            {
                public int Id { get; set; }
                public string Titulo { get; set; }
                public string Autor { get; set; }
    
                public string Editorial { get; set; }
                public int AnyoPublicacion { get; set; }
            }
    
            static void  Main(string[] args)
            {
                var listaLibros = new List<Libros>()
                {
                    new Libros ()
                    {
                        Id = 1, AnyoPublicacion = 2012, Autor = "Autor1", Editorial = "Editorial1", Titulo = "Titulo1"
    
                    },
                    new Libros ()
                    {
                        Id = 2, AnyoPublicacion = 2016, Autor = "Autor1", Editorial = "Editorial1", Titulo = "Titulo2"
    
                    },
                    new Libros ()
                    {
                        Id = 3, AnyoPublicacion = 2019, Autor = "Autor2", Editorial = "Editorial2", Titulo = "Titulo3"
    
                    }
                };
     
                int id = 1;
                IQueryable qry = listaLibros.AsQueryable(); 
                var where = "Id == @0"; // condiciones
                var select = "new (Id, Titulo)"; // qué campos deseamos recuperar. New te crea un tipo anónimo
                var libro = qry.Where(where, id).Select(select).AsEnumerable().FirstOrDefault();
                Console.WriteLine(libro);
                Console.ReadKey();
            }
        }
    }


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    lunes, 4 de mayo de 2020 17:51
    Moderador
  • Hola, 

    Gracias por tu respuesta. 

    ¿qué significa "@0"? Consultas parametrizadas. Es necesario y son buenas prácticas establecer las consultas con parámetros para evitar el sql injection y que no haya problemas al concatenar cadenas. 

    El @0 es el orden en la cadena de tu variable, empezando por 0, así que 

    var where = "Autor == @0, Editorial == @1 "; // FIJATE EN LOS PARAMETROS QUE SON 0 y 1....

    Lo cual es totalmente correcto pero, si ahora yo quiero meter en una variable el valor del campo "Título" y en otra el valor del campo "Autor", antes de pasarlos a la vista para, en lugar de pasar a la vista el contenido de la variable "libro", trabajar antes con los con los datos de forma independiente, cómo tendría que hacerlo?

    Como un objeto normal , si es que te entiendo bien, puedes mostrar un ejemplo? 

    var titulo = libro.Titulo; var autor = libro.Autor;


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta anespri miércoles, 6 de mayo de 2020 16:31
    miércoles, 6 de mayo de 2020 7:52
    Moderador
  • Ahhh!!!1 

    Deberías entonces recuperarlo vía Reflection

    Ejemplo

            public static object GetPropValue(this object src, string propName)
            {
                return src.GetType().GetProperty(propName).GetValue(src, null);
            }

    este es un método de Extensión que dado una propiedad en una cadena, te devuelve el valor de ese objeto

    Para ejecutarlo :

    var valor = miLibro.GetPropValue(miCampo)


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    jueves, 7 de mayo de 2020 13:58
    Moderador
  • Ok, probando he visto que se te genera un objeto "dynamic", entonces lo que te he comentado quedaría de la siguiente manera.

    var titulo = libro.GetType().GetProperty("Titulo").GetValue(libro,null);


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    viernes, 8 de mayo de 2020 14:39
    Moderador
  • Hola. Gracias por tus palabras. Te propongo que compartas esas líneas de código en este hilo para ayudar a más gente que tenga el mismo problema que tú. Un abrazo 

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta anespri lunes, 11 de mayo de 2020 14:29
    lunes, 11 de mayo de 2020 1:41
    Moderador

Todas las respuestas

  • Debería ser así tu código

    var miSelect = DynamicExpressionParser
                   .ParseLambda(typeof(MyModel),
                                null, 
                                "Id = d.Id, Titulo = d.Titulo, Autor = d.Autor, Editorial = d.Editorial");  
    



    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta anespri miércoles, 6 de mayo de 2020 16:28
    domingo, 3 de mayo de 2020 23:23
    Moderador
  • hola

    Si todo lo que vas a retornar es de la misma entidad (Libro) porque no la retornas completa? para que seleccionar solo algunas propiedades y complicarte la vida

    Aplicaria definir propiedades especificas si tomas algunas de una entidad, otras de una diferentes, o realizas alguna union entre valores de varias o calculos, pero para retornar solo algunas de la misma entidad es complicarte sin sentido

    usa directo

      Libros miConsulta = (from d in db.Libros

                                         where d.Id == id
                                         select d).FirstOrDefault();

    asi de facil, es mas es mas simple aun

      Libros miConsulta = db.Libros.FirstOrDefault(d=> d.Id == id);

    Despues accedes al Id, Titulo, Editorial, etc segun necesites

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    lunes, 4 de mayo de 2020 3:00
  • Hola, Sergio.

    Ante todo, muchas gracias por tu respuesta.

    Veo que por ahí pueden ir los tiros...

    En primer lugar, te pido disculpas por mi "ignorancia" en cuanto a LinQ Dynamic, estoy empezando con ello e igual planteo dudas con errores básicos.

    Supongo que ahora debo utilizar la variable que tú me has montado en el Select de la consulta LinQ. Algo parecido a lo que planteaba en mi pregunta:

                    Libros miConsulta = (from d in db.Libros

                                         where d.Id == id 
                                         select new Libros { miSelect }).FirstOrDefault();

    Debería ser algo así, o cual es la forma correcta de hacerlo?

    El motivo de querer hacer esto asi, es porque quiero hacer una sóla consulta que me sirva para distintos escenarios. y dependiendo del escenario, me muestre sólo unos campos u otros. Y los escenarios pueden variar dependiendo del item que se seleccione en un desplegable, del usuario que esté manejando la aplicación, etc...

    Y no quiero hacer una consulta para cada escenario. Quiero una sóla consulta en la que sólo varíe el Select.

    Una vez más, muchas gracias por tu tiempo y tu interés.

    lunes, 4 de mayo de 2020 16:47
  • Hola, Leandro.

    tal y como le he dicho a Sergio...

    El motivo de querer hacer esto asi, es porque quiero hacer una sóla consulta que me sirva para distintos escenarios. y dependiendo del escenario, me muestre sólo unos campos u otros. Y los escenarios pueden variar dependiendo del item que se seleccione en un desplegable, del usuario que esté manejando la aplicación, etc...

    Y no quiero hacer una consulta para cada escenario. Quiero una sóla consulta en la que sólo varíe el Select.

    Gracias de nuevo.

    lunes, 4 de mayo de 2020 16:49
  • Hola, he hecho pruebas y no hace falta hacer lo que estabas haciendo. Te pongo un ejemplo que me funciona en un app de consola

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Dynamic.Core;
    
    namespace proyhilobanco
    {
        public static class Program
        {
            
            public class Libros
            {
                public int Id { get; set; }
                public string Titulo { get; set; }
                public string Autor { get; set; }
    
                public string Editorial { get; set; }
                public int AnyoPublicacion { get; set; }
            }
    
            static void  Main(string[] args)
            {
                var listaLibros = new List<Libros>()
                {
                    new Libros ()
                    {
                        Id = 1, AnyoPublicacion = 2012, Autor = "Autor1", Editorial = "Editorial1", Titulo = "Titulo1"
    
                    },
                    new Libros ()
                    {
                        Id = 2, AnyoPublicacion = 2016, Autor = "Autor1", Editorial = "Editorial1", Titulo = "Titulo2"
    
                    },
                    new Libros ()
                    {
                        Id = 3, AnyoPublicacion = 2019, Autor = "Autor2", Editorial = "Editorial2", Titulo = "Titulo3"
    
                    }
                };
     
                int id = 1;
                IQueryable qry = listaLibros.AsQueryable(); 
                var where = "Id == @0"; // condiciones
                var select = "new (Id, Titulo)"; // qué campos deseamos recuperar. New te crea un tipo anónimo
                var libro = qry.Where(where, id).Select(select).AsEnumerable().FirstOrDefault();
                Console.WriteLine(libro);
                Console.ReadKey();
            }
        }
    }


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    lunes, 4 de mayo de 2020 17:51
    Moderador
  • Hola, Sergio.

    Esto va muy bien, es justo lo que necesitaba. Pero necesito que me aclares un par de cosas.

    Veo que incluso podemos meter en una variable el "where", para este caso no lo necesito porque siempre voy buscar por el campo Id, pero seguro que para otros procedimientos sí.

    Veo que al definir la variable "where" pones:

    var where = "Id == @0"; 

    ¿qué significa "@0"?

    Después veo que al definir la consulta pones:

    var libro = qry.Where(where, id)....

    Donde la variable "where" es la que hemos definido previamente como "Id == @0" y la variable "id" es la que previamente hemos definido en un punto anterior de la aplicación. Por qué se hace así y no se puede hacer de este modo?

     var where = "Id ==" id; 

    Y despues solamente...

    var libro = qry.Where(where)....

    Sólo es curiosidad, para entender perfectamente el funcionamiento. porque veo que realmente funciona como lo has planteado.

    Y si en lugar de sólo buscar por el campo Id, como es este caso, como tendríamos que hacer para buscar por el campo Editorial y por el Autor, por ejemplo.

    Sería algo así:

     var where = "Autor == @0, Editorial == @0 ";

    y despues...

    var libro = qry.Where(where, autor, editorial)....

    y por último, lo que hago es pasar por ViewBag el valor de la variable "libro" a una vista y en la vista me devuelve lo siguiente:

    { Id = 1000013, Titulo = El Quijote, Autor = Cervantes }

    Lo cual es totalmente correcto pero, si ahora yo quiero meter en una variable el valor del campo "Título" y en otra el valor del campo "Autor", antes de pasarlos a la vista para, en lugar de pasar a la vista el contenido de la variable "libro", trabajar antes con los con los datos de forma independiente, cómo tendría que hacerlo?

    Una vez más, agradezco tus respuestas, tu tiempo y tu interés y te pido disculpas por mi desconocimiento en cosas que quizas sean básicas.

    Saludos.

    martes, 5 de mayo de 2020 18:05
  • Hola, 

    Gracias por tu respuesta. 

    ¿qué significa "@0"? Consultas parametrizadas. Es necesario y son buenas prácticas establecer las consultas con parámetros para evitar el sql injection y que no haya problemas al concatenar cadenas. 

    El @0 es el orden en la cadena de tu variable, empezando por 0, así que 

    var where = "Autor == @0, Editorial == @1 "; // FIJATE EN LOS PARAMETROS QUE SON 0 y 1....

    Lo cual es totalmente correcto pero, si ahora yo quiero meter en una variable el valor del campo "Título" y en otra el valor del campo "Autor", antes de pasarlos a la vista para, en lugar de pasar a la vista el contenido de la variable "libro", trabajar antes con los con los datos de forma independiente, cómo tendría que hacerlo?

    Como un objeto normal , si es que te entiendo bien, puedes mostrar un ejemplo? 

    var titulo = libro.Titulo; var autor = libro.Autor;


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta anespri miércoles, 6 de mayo de 2020 16:31
    miércoles, 6 de mayo de 2020 7:52
    Moderador
  • Hola de nuevo Sergio.

    Ok, tomo nota de cómo se debe definir la variable "where". Sin duda la voy a utilizar en otros apartados de la aplicación.

    En cuanto a lo de tomar el valor de cada campo, también me funciona tal y como me lo planteas. Lo que pasa es que como el nombre del campo es dinámico, que me viene dado por una variable, no sé como hacerlo. Te lo explico mejor con un ejemplo:

    Imaginemos que quiero hacer una consulta de un libro en el que quiero que sólo me devuelva un campo de este libro pero, ese campo unas veces será el Autor, otras veces el Título, otras la Editorial, etc...

    Por lo tanto declaramos una variable llamada "miCampo".

    Esta variable tomará el valor "Autor", "Titulo", "Editorial", ... (Dependiendo de otros factores)

    Esa variable la pasamos en el Select de la consulta, tal y como tú me has dicho:

    var miWhere = "Id == @0";

    miSelect = "new (" + miCampo + ")";

    var miLibro = qry.Where(miWhere, id).Select(miSelect).AsEnumerable().FirstOrDefault();

    Y ahora lo que quiero es que me devuelva el valor del campo "miCampo" de "miLibro". Algo así:

    var miValor = miLibro.miCampo;

    Si pongo... "var miValor = miLibro.Titulo;", "var miValor = miLibro.Autor;", "var miValor = miLibro.Editorial;", tal y como me has dicho, funciona perfectamente. Pero como no sé el nombre del campo, lo que quiero es sustituir el nombre del campo por la variable "miCampo".

    Espero haberme explicado bien con este ejemplo.

    Una vez más, muchas gracias por tu respuesta. 

    miércoles, 6 de mayo de 2020 16:53
  • Ahhh!!!1 

    Deberías entonces recuperarlo vía Reflection

    Ejemplo

            public static object GetPropValue(this object src, string propName)
            {
                return src.GetType().GetProperty(propName).GetValue(src, null);
            }

    este es un método de Extensión que dado una propiedad en una cadena, te devuelve el valor de ese objeto

    Para ejecutarlo :

    var valor = miLibro.GetPropValue(miCampo)


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    jueves, 7 de mayo de 2020 13:58
    Moderador
  • Hola, Sergio.

    Esto es justo lo que necesito. Pero no he sido capaz de implementarlo. No sé en qué punto del código tengo que incluir esto:

            public static object GetPropValue(this object src, string propName)
            {
                return src.GetType().GetProperty(propName).GetValue(src, null);
            }

    Lo he probado en todos los diferentes puntos posibles del código del controlador y en todos me da algun error.

    Además, he añadido "using System.Reflection;" por si hiciera falta, pero también me sigue dando error.

    También quisiera saber si lo tengo que escribir tal cual o si tengo que sustituir algun valor por el de alguna variable u objeto de mi código.

    A ver si con esto ya lo dejamos terminado.

    Muchas gracias.


    • Editado anespri viernes, 8 de mayo de 2020 8:36
    viernes, 8 de mayo de 2020 8:33
  • Ok, probando he visto que se te genera un objeto "dynamic", entonces lo que te he comentado quedaría de la siguiente manera.

    var titulo = libro.GetType().GetProperty("Titulo").GetValue(libro,null);


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    viernes, 8 de mayo de 2020 14:39
    Moderador
  • Hola, Sergio.

    Al final todo mi problema se ha reducido a 3 líneas de código. Enhorabuena y muchas gracias. Lo hemos conseguido!!!

    No es que yo sea un experto en la materia, pero la experiencia me dice que el éxito de la programación está en la sencillez y este es un claro ejemplo.

    Con estas simples líneas voy a intentar hacer mucho más de lo que imaginas.

    Aunque hemos estado trabajando con un ejemplo muy sencillo de una colección de libros, para hacer más entendible mi planteamiento, mi objetivo va mucho más allá. Con estas líneas de código pretendo administrar todas las tablas de la estructura de una aplicación que gestione proyectos de un grupo de empresas con sus delegaciones, departamentos, usuarios, etc... Pretendo que una consulta dinámica me sirva para todo. No sé si lo conseguiré, pero gracias a tu aportación la cosa va por buen camino.

    Una vez más, muchas gracias.

    Seguramente aparezca más veces por aquí con más dudas.


    Tony

    lunes, 11 de mayo de 2020 1:04
  • Hola. Gracias por tus palabras. Te propongo que compartas esas líneas de código en este hilo para ayudar a más gente que tenga el mismo problema que tú. Un abrazo 

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    • Marcado como respuesta anespri lunes, 11 de mayo de 2020 14:29
    lunes, 11 de mayo de 2020 1:41
    Moderador