How to build custom queries using Entity Framework?

Asked

Viewed 1,423 times

7

I have an SQL Server database that is accessed via ORM Entity Framework Core, where access to a collection of products, these products are filtered through a DTO that has the terms of the search, which can be composed or simple, example: the customer can inform in the search only the name of the product or Name, Manufacturing material and UF.

The question is how to assemble a query readable to cover all customizable scenarios?

DTO:

public class DtoPesquisaProduto
{
    public string Nome { get; set; }

    public string Material { get; set; }

    public EstadosEnum? Estado { get; set; }

    public decimal PrecoInicial { get; set; }

    public decimal PrecoFinal { get; set; }
}

The Consultation:

_context.Produtos 
.Where(x => x.Nome == pesquisa.Nome 
    && x.Material == pesquisa.Material 
    && x.Estado == pesquisa.Estado 
    && (x.Preco >= pesquisa.PrecoInicial && x.Preco <= pesquisa.PrecoFinal)) 
 .ToListAsync();

The consultation Where should be dynamic considering only the expression whose value is different from null.

4 answers

17


One thing few people understand is that LINQ is an expression constructor. He’s not running all that at once, he does it one step at a time, as long as he’s in different methods. Your code is, in essence, the same as:

IQueryable<Produtos> query = _context.Produtos;
query = query.Where(x => x.Nome == pesquisa.Nome);
query = query.Where(x => x.Material == pesquisa.Material);
query = query.Where(x => x.Estado == pesquisa.Estado);
query = query.Where(x => x.Preco >= pesquisa.PrecoInicial && x.Preco <= pesquisa.PrecoFinal);
resultado = query.ToListAsync();

Now just put a if for each row to decide whether the expression will enter or not. Example:

IQueryable<Produtos> query = _context.Produtos;
if (pesquisa.Nome != null) query = query.Where(x => x.Nome == pesquisa.Nome);
if (pesquisa.Material != null) query = query.Where(x => x.Material == pesquisa.Material);
... //aqui coloca tudo o que deseja
resultado = await query.ToListAsync();

I put in the Github for future reference.

And think if really the Async() is the best option, in many cases it gets worse, especially if it is not used if correct form. And wonder if this is the right time to give the ToList(), It is very common for people to give before what they should for not understanding LINQ. _ is not idiomatic in C#. But these are other issues.

This works well for purely and, but if you need something more complex then you can use a predicate Builder or library that builds dynamically for you.

  • 1

    Sincerity I work with EF for a long time and what is explained above does not make much sense, I wanted to really understand before putting a plausible opinion, since Tolistasync at the end of the code will not have the later filters! The most correct answer is that of Marcelo who took negative votes without need (perhaps because of Ienumerable, maybe)

  • I questioned the ToListAsync(), I just left what he’s doing. In fact I forgot to put the assignment. Good you questioned why I hadn’t noticed. That of Marcelo uses HasValue in fields that are clearly by reference, and have a method that only he knows what he does, and the error you pointed out. then I don’t know how this can be right.

  • Good _context would not be a legal assignment, is the variable DBContext, should be made for another variable by question. I can agree that there are some points that improve the response of the friend, but, in compensation to his before was the closest to reality, including Maniero, has duplicate, I’ve seen, but, I can not find by the search system.

  • I think you’re right. If there is something else you saw wrong I improve. Thank you for the questions, only contributed to a better result for all. Really the search is bad ,if you find brand there I finish closing.

  • Maniero for this your function work you have to change from Iqueryable<Product> to Iqueryable<Products> and change from query.Product.Where to query. Where later in subsequent query.

  • @Lucianooiticicalemgruber yes, I had these mistakes and I corrected now, thank you, I recognize my mistakes and who helps me. I just don’t understand why the same mistake in another answer gets congratulated.

Show 1 more comment

3

The .Where(this IEnumerable<T>, Predicate<T>) are loaded lazily, that is, they are not processed at the time of the declaration, but rather when the final consultation (the final result of the .Where()) is iterated.

IQueryable<Produto> query = _context.Produtos;

if (!string.IsNullOrEmpty(pesquisa.Nome))
{
    query = query.Where(where => where.Nome == pesquisa.Nome);
}

if (!string.IsNullOrEmpty(pesquisa.Material))
{
    query = query.Where(where => where.Material == pesquisa.Material);
}

if (pesquisa.Estado.HasValue)
{
    query = query.Where(where => where.Estado == pesquisa.Estado.Value);
}

// ... assim por diante, para cada propriedade

return query.ToList(); // Materializa query.
  • 2

    IEnumerable<Produto> query = _context.Produtos; the correct would be IQueryable<Produto> query = context.Produtos;, if you change that your answer is correct and the funniest take negative votes.

  • 1

    For the sake of ethics and equality, why put a return Mapear(query) in your reply? is not part of the original question, nor is it part of a method or class of your question, could explain or even remove by placing a query.ToList() in the end. ?

  • I agree with Virgilio Novic that is the best answer, I reworded my reply based on his comments Virgilio Novic.

  • @Lucianooiticicalemgruber and Virgil don’t agree, he presented two problems in it, and I presented one more. Anyway thank you for agreeing to my reply since the new version of yours does the same as in mine.

  • Changed to IQueryable<Produto>.

  • Changed to query.ToList();.

Show 1 more comment

0

Hello Ricardo good night.

Come on you must want it within the same correct query ?

for this you must use a ternary operator

await _context.Produtos 
.Where(x => pesquisa.Nome != null ? x.Nome == pesquisa.Nome : false)
.Where(x => pesquisa.Material  != null ? x.Material == pesquisa.Material : false)
.Where(x => pesquisa.Estado != null ? x.Estado == pesquisa.Estado : false)
.Where(x => pesquisa.PrecoInicial != null ? x.Preco >= pesquisa.PrecoInicial : false)
.Where(x => pesquisa.PrecoFinal != null ? x.Preco <= pesquisa.PrecoFinal : false)
.ToListAsync();

A bit about ternary operator with Master Macoratti http://www.macoratti.net/14/11/c_tern1.htm

This way you will search for all attributes in the same query.

UPDATE

I apologize I had put a true at the end that picks null values from the table.

Use false that it will not fetch null values from the table.

Below is an example of the query generated in a table of mine with the true

.Where(x => pesquisa.PrecoInicial != null ? x.Preco >= pesquisa.PrecoInicial : true)

SELECT [a].[ViaturasID],[a].[ValorPrestacao]
FROM [Viaturas] AS [a]
WHERE ((@__prestacaoInicial_0 IS NOT NULL AND ([a].[ValorPrestacao] >= @__prestacaoInicial_1)) OR @__prestacaoInicial_0 IS NULL) AND ((@__prestacaoFinal_2 IS NOT NULL AND ([a].[ValorPrestacao] <= @__prestacaoFinal_3)) OR @__prestacaoFinal_2 IS NULL)

and with the false

.Where(x => pesquisa.PrecoInicial != null ? x.Preco >= pesquisa.PrecoInicial : false)

SELECT [a].[ViaturasID],[a].[ValorPrestacao]
FROM [Viaturas] AS [a]
WHERE (@__prestacaoInicial_0 IS NOT NULL AND ([a].[ValorPrestacao] >= @__prestacaoInicial_1)) AND (@__prestacaoFinal_2 IS NOT NULL AND ([a].[ValorPrestacao] <= @__prestacaoFinal_3))

================ NEW SOLUTION ================

Based on the comments of Virgilio Novic I really saw that this will make the consultation more effective.

Since this came all from the reply of Marcelo Uchimura.

IQueryable<Produtos> query = _context.Produtos;

if (pesquisa.Nome != null)
{
    query = query.Where(x => x.Nome == pesquisa.Nome);
}
if (pesquisa.Material  != null)
{
    query = query.Where(x => x.Material == pesquisa.Material);
}
if (pesquisa.Estado != null)
{
    query = query.Where(x => x.Estado == pesquisa.Estado);
}
if (pesquisa.PrecoInicial != null)
{
    query = query.Where(x => x.Preco >= pesquisa.PrecoInicial);
}
if (pesquisa.PrecoFinal != null)
{
    query = query.Where(x => x.Preco <= pesquisa.PrecoFinal);
}
query.ToList();

Example of query generated in a table of mine

SELECT [m].[ViaturasID],[m].[ValorPrestacao]
FROM [Viaturas] AS [m]
WHERE ([m].[ValorPrestacao] >= @__prestacaoInicial_0) AND ([m].[ValorPrestacao] <= @__prestacaoFinal_1)

If you want to use . Tolistasync() change the ending to

await query.ToListAsync();
  • 1

    This will be extremely inefficient and will generate a query terrible.

  • If you think a query that has unnecessary checks is clean, and that because of this can not use possible indexes, then is.

  • I’m here to try to help. If he wants to search for all the parameters together everyone has to be in the same query. He does not want to search by Name = XX or Material = Y. He wants to search Name = X and Material = Y.

  • 1

    No one questioned whether you’re here to help or not. He wants to mount a condition dynamically, he has several fields in a structure in memory, so if the data is not null he must enter the condition, if the data is not null he has no reason to use it, he must not enter the condition, his everything is entering the condition. Try to generate the query with 2 or more conditions.

  • 1

    Yes, I showed you a problem in the code, you started asking speculative questions, and you continue, the hostility part of your part that wants to see problem in a simple questioning of a problem in your code. If you stick to the problem and the code and not to the people, hostility ceases, and so you do not create discussion that you do not want. If someone has disrespected you signal the post that another moderator will assess the situation. Here at Sopt we evaluate the solutions to always get the best possible, if you do not want people to evaluate what you put will have problems here.

0

 await _context.Produtos 
.Where(x => (pesquisa.Nome == null || x.Nome == pesquisa.Nome)
       &&   (pesquisa.Material  == null || x.Material == pesquisa.Material)
       &&   (pesquisa.Estado == null || x.Estado == pesquisa.Estado)
       &&   (pesquisa.PrecoInicial == 0 || x.Preco >= pesquisa.PrecoInicial)
       &&   (pesquisa.PrecoFinal == 0 || x.Preco <= pesquisa.PrecoFinal))
.ToListAsync();``

Make it equal to a query in SQL, if it is null it will not do anything in the query, if there is data it will make the comparison of each of them. Note: Decimal value comparison == 0 because it cannot be null.

  • This way the nulls will be part of the consultation and is not what he wants.

  • @Maniero no, that way if a parameter comes null he will do nothing, if it is different from null he makes the comparison

  • You did not understand the question, it will be part of the query, it is inefficient, it generates a bad query, I already commented on the other answer. AP wants the query to be built only with existing data, which is efficient, so it needs to generate dynamically, if so is not a dynamic generation, is static with subsequent checking on all lines of something that can be done only once before and that will probably prevent the use of an index, and it can all be tragic.

  • I do not agree with your point of view, the validation is of the search variable, if it is null it will not be part of the query, I have used many times this way and never generated slow queries or something like that, but I may be wrong or really have not understood the question.

  • It’s not point of view, it’s how EF works. You understand how it generates the query? And did you measure it using that way and the right way? Because what I see most nowadays is that people use worse things and don’t even notice.

  • 1

    I understand how the query works, I have tested and do several of the same in gigantic and complex systems, but that’s it buddy, if you know more than I do all right, I’m here to try to help just, don’t be rude assuming you know everything and people are laymen.

  • No one was being rude, until now, I’m just disagreeing with what you posted, can’t you? Do you have to accept what you say without contesting it? Only you know and no one can say otherwise? Show where I assumed I know everything and you know nothing? I even asked a question to understand your knowledge. Well, I’ve shown the problem of the answer so other people know that this is not appropriate, now it’s up to them to understand whether you’re right or not, it would be better if this problem was solved, but you can leave it as you see fit.

Show 2 more comments

Browser other questions tagged

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