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

6

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

Table Pessoa :

  • Primary key field: id_pessoa
  • Field name: nome
  • Field age: idade

Class Pessoa built from the% database% table by EF:

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 query:

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 question

In the pessoa method, I would like to know if it is possible to change the query structure made in LINQ at runtime to append one more condition, which in this case is the second parameter of the lerPessoa(string nome, bool maiorIdade) method, in case the value of the variable maiorIdade is maiorIdade the condition true must be specified in the p.idade >= 18 clause.

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

    
asked by anonymous 27.05.2016 / 01:17

3 answers

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 was changed to var so that it does not force the query to materialize.

Another way to help with 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 SQL generation. As I have no experience with EF I can not explain the reason, but it makes sense since there is spurious information in query . According to comments below, Gypsy also understands the same, but gave no explanation of why. I do not recommend using EF.

What you may want to simplify is to use imperative form instead of declarative . The declarative form is cute, but often it turns out to be less concise than the imperative form. The solution might be 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 do not see a better way.

    
27.05.2016 / 01:31
5

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

It looks 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
          //de um nome para o obejto, no meu caso utilizei "p". O objeto deve serguir 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çãm 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);
} 
    
27.05.2016 / 01:54
4

For situations like this, I like to extend the LINQ class and use WhereIf . The nice thing about this method is if you have control over what will be sent to the ORM, and if it does not resolve it, it will send clauses that could be ignored to the database.

I wrote about this in this answer .

First add the extension in 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();
    
27.05.2016 / 14:14