Func<T, bool> and Expression<Func<T, bool>>

Asked

Viewed 2,138 times

3

Well I’d like to know first what the difference is between Func<T, bool> for Expression<Func<T, bool>>

And how do I convert from Func<T, bool> for Expression<Func<T, bool>> if it is possible.

Or I need all my code to be Expression instead of Func. But I don’t know how to Convert and concatenate Expression. In the Filter method().

public class MovimentoTratamento : Tratamento
{
    public string Nr_Nota_Fiscal { get; set; }
    public string Dt_Emissao_Nota_Fiscal { get; set; }
}

public abstract class Tratamento
{
    [Key]
    public int ID{ get; set; }
    public string Cd_Sap_Distribuidor_Matriz { get; set; }
    public string Cd_Sap_Distribuidor_Filial { get; set; }
    public DateTime Dt_Criacao { get; set; }
    public int Id_Log { get; set; }
}

private Func<Tratamento, bool> FiltroPadrao(FiltroViewModel filtroViewModel)
    {
        DateTime inicial, final;

        DateTime.TryParse(filtroViewModel.CargaInicial, out inicial);
        DateTime.TryParse(filtroViewModel.CargaFinal, out final);

        Func<Tratamento, bool> f = x =>
                                       ((final == DateTime.MinValue) || (x.Dt_Criacao >= inicial && x.Dt_Criacao <= final.AddDays(1)))
                                       &&
                                       (string.IsNullOrEmpty(filtroViewModel.CnpjMatriz) || x.Cd_Sap_Distribuidor_Matriz == filtroViewModel.CnpjMatriz)
                                       &&
                                       (string.IsNullOrEmpty(filtroViewModel.CnpjFilial) || x.Cd_Sap_Distribuidor_Filial == filtroViewModel.CnpjFilial)
                                       &&
                                       (filtroViewModel.TipoErro == "0" || x.Cd_Erro == filtroViewModel.TipoErro)
                                       &&
                                       (
                                            (filtroViewModel.StatusSelected == 3) ||
                                            (filtroViewModel.StatusSelected == 1 && x.Id_Log == 0) ||
                                            (filtroViewModel.StatusSelected == 2 && x.Id_Log > 0)
                                       );
        return f;
    }

    private Func<MovimentoTratamento, bool> FiltroNotaFiscal(FiltroViewModel filtroViewModel)
    {
        return x => (string.IsNullOrEmpty(filtroViewModel.NotaFiscal) || x.Nr_Nota_Fiscal == filtroViewModel.NotaFiscal);
    }

    public IQueryable<MovimentoTratamento> Filter()
    {
        Func<Tratamento, bool> filter = FiltroPadrao(vm);
        Func<MovimentoTratamento, bool> mov = FiltroNotaFiscal(vm);
        UnirExpressionMovimento(filter, mov);
        return repo.Get(f);
    }

    private Func<MovimentoTratamento, bool> UnirExpressionMovimento(Func<Tratamento, bool> filter, Func<MovimentoTratamento, bool> mov)
    {
        Func<MovimentoTratamento, bool> f = value => filter((Tratamento)value);

        return value => f(value) && mov(value);
    }
  • related, http://answall.com/questions/2822/o-que-s%C3%A3o-lambda-Expressions-e-qual-a-sacada-em-us%C3%A1-las/2834#2834

  • @Diegozanardo, as much as I’ve tried to help you with your problem, I disagree with your architecture, I don’t think it’s a good idea to implemenar Repository Pattern additional on the Entity Framework, you can read more about it in When to use Entity Framework with Repository Pattern?

  • still addressing this subject, avoid creating something very complex, it would be much simpler if you perform this direct query on your DataSet, without the need to create generic functions... although it is not very connected to your problem, see this article/video: Stack Overflow and Stack Exchange Architecture

1 answer

3


Original Response: Why would you use Expression> rather than Func?

Both do different things, while the Func<T, bool> represents a code that will be executed, so much so that the code will be converted to intermediate language and possibly executed.

The Expression<Func<T, bool>> contains a representation of what needs to be done, and not necessarily the code that will be executed, for example, the following chunk of a query using Entity Framework (TSQL):

context.Entities.Where((x) => x.Propriedade1 == x.Propriedade2);

In the above case, the (x) => x.Propriedade1 == x.Propriedade2 will not run, but it contains information of what needs to be done, in this case it will be converted to a query SQL => SELECT * FROM Entities x WHERE x.Propriedade1 == x.Propriedade2

Note that the input and output of both may even be equal, but the way they work, the compiled code and what they are intended for is completely different.

now returning to the example above, if you want to reuse a Func<T, bool> to be used in the Where (which expects a Expression<Func<T, bool>>), you will need to make a function encapsulamente:

var funcao = new Func<Entity, bool>((x) => x.Propriedade1 == x.Propriedade2);
context.Entities.Where((x) => funcao(x));

note that in this case, the funcao(x) is not executed, but it contains the "instructions" that the Expression need, then she’ll understand what needs to be done.

Now I would like to talk a little about its implementation and what I disagree with it... the first thing is about this variable repo, seems to be using some implementation of Repository Pattern together with the Entity Framework.

Instead simply use your own Entity Framework, which in turn already implements Repository Pattern in a much more robust and complete way than you would be able to do it.

Second point, if you really want to isolate snippets of use query to reuse them later, then make use of Extensions.

public class MovimentoTratamento : Tratamento
{
    public string Nr_Nota_Fiscal { get; set; }
    public string Dt_Emissao_Nota_Fiscal { get; set; }
}

public abstract class Tratamento
{
    [Key]
    public int ID{ get; set; }
    public string Cd_Sap_Distribuidor_Matriz { get; set; }
    public string Cd_Sap_Distribuidor_Filial { get; set; }
    public DateTime Dt_Criacao { get; set; }
    public int Id_Log { get; set; }
}

public static class TratamentoExtensions
{
    public static IQueryable<Tratamento> FiltroPadrao(this IQueryable<Tratamento> consulta, FiltroViewModel filtro)
    {
        DateTime inicial, final;
        DateTime.TryParse(filtro.CargaInicial, out inicial);
        DateTime.TryParse(filtro.CargaFinal, out final);

        return consulta.Where(x =>
            ((final == DateTime.MinValue) || (x.Dt_Criacao >= inicial && x.Dt_Criacao <= final.AddDays(1))) &&
            (string.IsNullOrEmpty(filtro.CnpjMatriz) || x.Cd_Sap_Distribuidor_Matriz == filtro.CnpjMatriz) &&
            (string.IsNullOrEmpty(filtro.CnpjFilial) || x.Cd_Sap_Distribuidor_Filial == filtro.CnpjFilial) &&
            (filtro.TipoErro == "0" || x.Cd_Erro == filtro.TipoErro) &&
            ((filtro.StatusSelected == 3) || (filtro.StatusSelected == 1 && x.Id_Log == 0) ||   (filtro.StatusSelected == 2 && x.Id_Log > 0));
    }

    private IQueryable<MovimentoTratamento> FiltroNotaFiscal(this IQueryable<MovimentoTratamento> consulta, FiltroViewModel filtro)
    {
        return consulta.Where(x => (string.IsNullOrEmpty(filtro.NotaFiscal) || x.Nr_Nota_Fiscal == filtro.NotaFiscal));
    }
}

public class TratamentoController
{
    public IQueryable<MovimentoTratamento> Filter(FiltroViewModel filtro)
    {
        using (var context = new MyDbContext())
        {
            return context.MovimentosTratamento.FiltroPadrao(filtro).FiltroNotaFiscal(filtro));
        }
    }
}

The reasons I don’t recommend using a Repository Pattern additional, you can see on the following link When to use Entity Framework with Repository Pattern?

I also recommend reading the following article: Stack Overflow and Stack Exchange Architecture

  • The correct one would then I convert all my Func code to Expression. But in parts of the code I concatenate Func and cast a T. How would I do this using Expression?

  • @Diegozanardo, you can try calling your Func<T, bool> directly inside the Expression<T, bool>, but understand that this involves a risk, after all some snippets of code may not have a translation, for example when using the Entity Framework you may come across a NotSupportedException when using an unexpected code block.

  • It’s exactly the Entity Framework that I’m using. So this won’t happen?

  • @Diegozanardo, you will need to test, if it doesn’t work, try to identify the unsupported code snippet.

  • I’ll take this test soon... A doubt that I was left, using the Entity Framework, passing to it an Func, when fetching the data, it will bring all the records and then filter, or will bring the filtered result?

  • @Diegozanardo, if you make one .Where((x) => funcao(x)), it will already bring filtered from the bank.

  • Failed. The LINQ Expression Node type 'Invoke' is not supported in LINQ to Entities.

  • @Diegozanardo, how are you utilizing? .Where(x => FiltroPadrao(filtro)(x))?

  • Ref. Get((x) => f(x))

Show 4 more comments

Browser other questions tagged

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