How to change the structure of a LINQ query at runtime?

Asked

Viewed 414 times

7

I have the following example of a program that implements the EF (Entity Framework). The structure of the database and the application is defined as follows:

Table Pessoa:

  • Primary key field: id_pessoa
  • Name field: nome
  • Old field: idade

Class Pessoa constructed from the table pessoa of the database by the RU:

namespace EFPessoaExemplo
{
    using System;
    using System.Collections.Generic;
    
    public partial class Pessoa
    {
        public int id_pessoa { get; set; }
        public string nome { get; set; }
        public int idade { get; set; }
    }
}

And the method responsible for the Linq consultation:

private void lerPessoa(string nome, bool maiorIdade) 
{
    using (BancoEFEntities context = new BancoEFEntities()) 
    {
        IEnumerable<Pessoa> pessoa = from p in context.Pessoa 
                                     where p.nome.Contains(nome) 
                                     select p;

        dataGridViewPessoa.DataSource = pessoa.ToList();
    }
}

My doubt

In the method lerPessoa(string nome, bool maiorIdade), i wonder if it is possible to change the structure of the LINQ query at runtime to add one more condition, which in this case is the second parameter of the method maiorIdade, if the value of the variable maiorIdade be it true the condition p.idade >= 18 the clause shall be specified where.

There is a way to do this without having to create an entire LINQ query for each condition?

3 answers

5

In the example below I use Linq with Lambda expressions not to repeat the code.

Stay like this:

private void lerPessoa(string nome, bool maiorIdade) 
{
    using (BancoEFEntities context = new BancoEFEntities()) 
    {
      var query = from p in context.Pessoa 
                                 where p.nome.Contains(nome) 
                                 select p;

       if(maiorIdade)
          //aqui minha query vai receber uma nova comparação
          //por isso usa-se o query.Where
          //dê um nome para o objeto, no meu caso utilizei "p". O objeto deve seguir de =>
          //depois de fazer isso o Where espera uma expressão booleana
          //ou seja dentro do Where sempre tem que ter uma expressão de comparação
          //agora será filtrado os objetos que satisfaçam a comparação
          //portanto quando você der um query.ToList() retornará apenas pessoas que tenham idade acima de 17 anos.
          query = query.Where(p=> p.idade >= 18);

       dataGridViewPessoa.DataSource = query.ToList();
    }
}

Plan your code for reuse

private IList<Pessoa> listarPessoas(string nome, bool maiorIdade) {
   using(var context = new BancoEFEntities()) {

       var query = from p in context.Pessoa
                                     where p.nome.Contains(nome)
                                     select p;
       if(maiorIdade)
           query = query.Where(p=> p.idade >=18);
       return query.ToList();
   }
}


private void SeuMétodo()
{
   gridView.DataSource = lerPessoa('nome', false);
} 
  • 1

    It would be interesting for you to explain that Where, is not only Linq and yes Lambda Expression with Linq, only to be clearer to those who do not know. The solution itself is perfect.

  • Thanks Fernando, I’ll make this adjustment.

4


I think this is what you want:

private void lerPessoa(string nome, bool maiorIdade) {
    using (BancoEFEntities context = new BancoEFEntities()) {
        var pessoa = from p in context.Pessoa 
                                     where p.nome.Contains(nome) 
                                     select p;
        if (maiorIdade) {
            pessoa = from p in pessoa
                     where p.idade >= 18
                     select p;
        }
        dataGridViewPessoa.DataSource = pessoa.ToList();
    }
}

LINQ is applicable to any enumerable structure. The result of a LINQ query is an enumerable structure. Note that the statement has been changed to var so that the implementation of the query.

Another way to help the problem as per AP comment:

private void lerPessoa(string nome, bool maiorIdade) {
    using (BancoEFEntities context = new BancoEFEntities()) {
        IEnumerable<Pessoa> pessoa = from p in context.Pessoa 
                                     where p.nome.Contains(nome) && (!maiorIdade || p.idade >= 18)
                                     select p;
        dataGridViewPessoa.DataSource = pessoa.ToList();
    }
}

This form can be problematic for the generation of SQL. As I have no experience with EF do not know explain the reason, but it makes sense since there is spurious information in query. As comments below, the Gypsy also understands the same, but did not give explanation of the reason. I do not advise the use in EF.

What you might want to simplify is to use the mandatory form instead of the declarative form. The declarative form is cute, but often it ends up being less concise than the imperative form. The solution then perhaps is to change the form of writing:

private void lerPessoa(string nome, bool maiorIdade) {
    using (BancoEFEntities context = new BancoEFEntities()) {
        var pessoa = context.Pessoa.Where(p => p.nome.Contains(nome));
        if (maiorIdade) pessoa = pessoa.Where(p => p.idade >= 18);
        dataGridViewPessoa.DataSource = pessoa.ToList();
    }
}

I put in the Github for future reference.

I can’t think of a better way.

  • In this case, will it be necessary to rewrite the basic structure of the LIN for each condition? Would it be possible to make a concatenation like the Sqls queries?

  • 1

    @Yes, you can, I added.

  • Some answer here in Sopt that explains what is an enumerable structure?

  • 1

    Probably http://answall.com/q/17284/101. This help a little http://answall.com/q/125601/101 and also http://answall.com/q/129810/101 and finally http://answall.com/q/44293/101. If none satisfy, then not there :)

  • This answer is incorrect. The author of the question wants dynamic SQL generation. The first proposal makes two disjoint SQL’s, and the second is a technical artifice.

  • @Is Gypsy Rrisonmendez true, but is there a problem on the second? Are the other answers correct? Why?

  • You’re solving every case for IEnumerable, running SQL. That’s not what the questioner wants. He wants to conditionally mount SQL before running.

  • @Ciganomorrisonmendez But are the other answers right? Just because there is no defined type?

  • No, it’s not just that. The essence of the answer is not to use IEnumerable. How do you know, IEnumerable forces the resolution of the entire sentence. Not only the IEnumerable like the ToList(). The second case is even worse because you are creating a condition that does not need to go to SQL.

  • @Ciganomorrisonmendez then did not understand why the other answers are correct if deep down they do the same thing, only they used the var.

  • 2

    Because the var uses dynamic inference. Does not solve for IEnumerable and therefore does not execute the search. The search is only executed in the ToList().

  • @Ciganomorrisonmendez so far I agree and I’ll fix, but you said it’s not the only problem.

  • This is a huge problem: where p.nome.Contains(nome) && (!maiorIdade || p.idade >= 18)

  • @Ciganomorrisonmendez E does not the same for http://answall.com/a/130811/101?

  • No. Extension method does not return IEnumerable.

  • @Ciganomorrisonmendez all right, this problem I understand, I did not understand why the expression is problematic and the extension method that does the same thing is not.

Show 11 more comments

4

For situations like this, I like to extend the LINQ class and use the WhereIf. The cool thing about this method is whether or not you have control of what the ORM will or will not solve - and if it doesn’t, it will send to the database clauses that could be ignored.

I wrote about it in this answer.

First add the extension to your project:

public static class LinqExtensions
{
    public static IQueryable<TSource> WhereIf<TSource>(
        this IQueryable<TSource> source, bool condition,
        Expression<Func<TSource, bool>> predicate)
    {
        if (!condition) return source;
        return source.Where(predicate);            
    }
}

Then use wisely:

// a clausula WHERE somente será aplicado se variável 'condicao' for true
var resultado = MeusDados
                    .WhereIf(condicao == true, dado => dado.campo == "valor")
                    .ToList();
  • 1

    In this question Yes the WhereIf fits, hehe! Dynamic closures. + 1

Browser other questions tagged

You are not signed in. Login or sign up in order to post.