Compartilhar via


Traduzindo funções para o servidor com EntityFramework Core

https://i0.wp.com/ralmsdeveloper172900781.files.wordpress.com/2017/12/efcore.png

Traduzindo funções no EntityFramework Core

Olá tudo bem?!

Uma de várias novidades que surgiu no EntityFramework  Core 2.0 foi o suporte para UDF, a utilização das funções predefinas no banco de dados, isso facilita muita coisa, a forma que o EFCore oferece esse recurso tornou mais fácil a utilização das funções existentes em nossos bancos de dados.

Nesse pequeno artigo irei mostrar para você como fazer a utilização desse recurso, vale a pena ressaltar, que essas funões serão traduzidas apenas no servidor, e não será avaliada do lado cliente, mais isso pode ser possível também, mais aqui irei apenas demonstrar como traduzir isso para o servidor.

Na próxima versão EFCore 2.1, sairá com algumas funções para DATEDIFF já implementadas em EF.Functions.

Nosso primeiro exemplo será traduzir o DATEDIFF do SQL Server, o mesmo estará disponível na versão 2.1, porém nosso intuito aqui é mostrar como traduzir funções usando o EF Core.

Primeiramente iremos criar uma classe para organizar nossas funções:

public class  Funcoes
{
    public static  int SqlDateDiff(string tipo, DateTime inicio, DateTime fim)
        => 0;
 
    public static  string SqlLeft(object dados, int limite)
        => string.Empty;
 
    public static  string SqlReplace(object dados, string substituir, string por)
        => string.Empty;
}

No método OnModelCreating é onde iremos criar nossas expressões que serão traduzidas no servidor.

Primeira função (DATEDIFF):

builder
    .HasDbFunction(typeof(Funcoes)
    .GetMethod(nameof(Funcoes.SqlDateDiff)))
    .HasTranslation(args =>
    {
        var argumentos = args.ToList();
        argumentos[0] = new  SqlFragmentExpression((string)((ConstantExpression)argumentos.First()).Value);
        return new  SqlFunctionExpression(
            "DATEDIFF",
            typeof(int),
            argumentos);
    });

Utilização da Função:

var teste01 = db
    .Tests
    .Where(p => Funcoes.SqlDateDiff("DAY", DateTime.Now, DateTime.Now) == 0)
    .ToList();

Query Gerada no Servidor:

SELECT [p].[Id], [p].[Data], [p].[Nome]
FROM [Tests] AS [p]
WHERE DATEDIFF(DAY, GETDATE(), GETDATE()) = 0

Segunda função (LEFT):

builder
    .HasDbFunction(typeof(Funcoes)
    .GetMethod(nameof(Funcoes.SqlLeft)))
    .HasTranslation(args =>
    {
        var argumentos = args.ToList();
        return new  SqlFunctionExpression(
            "LEFT",
            typeof(string),
            argumentos);
    });

Utilização da Função:

var teste02 = db
    .Tests
    .Select(p => new  { TestLeft = Funcoes.SqlLeft(p.Nome, 20) })
    .ToList();

Query Gerada no Servidor:

SELECT LEFT([p].[Nome], 20) AS  [TestLeft] 
FROM [Tests] AS [p]

Terceira função (REPLACE):

builder
    .HasDbFunction(typeof(Funcoes)
    .GetMethod(nameof(Funcoes.SqlReplace)))
    .HasTranslation(args =>
    {
        var argumentos = args.ToList();
        return new  SqlFunctionExpression(
            "REPLACE",
            typeof(string),
            argumentos);
    });

Utilização da Função:

var teste03 = db
    .Tests
    .Select(p => new  { TestReplace = Funcoes.SqlReplace(p.Nome, "A", "B") })
    .ToList();

Query Gerada no Servidor:

SELECT REPLACE([p].[Nome], N'A', N'B') AS  [TestReplace]
FROM [Tests] AS [p]

**Todo código utilizado em nossos exemplos estão aqui:

**

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using System;
using System.Linq;
using System.Linq.Expressions;
 
namespace FunctionsEFCore2
{
    class Program
    {
        static void  Main(string[] args)
        {
            using (var db = new ExemploDb())
            {
                db.Database.EnsureCreated();
 
                var teste01 = db
                    .Tests
                    .Where(p => Funcoes.SqlDateDiff("DAY", DateTime.Now, DateTime.Now) == 0)
                    .ToList();
 
                var teste02 = db
                    .Tests
                    .Select(p => new  { TestLeft = Funcoes.SqlLeft(p.Nome, 20) })
                    .ToList();
 
                var teste03 = db
                    .Tests
                    .Select(p => new  { TestReplace = Funcoes.SqlReplace(p.Nome, "A", "B") })
                    .ToList();
            }
        }
    }
 
    public class  ExemploDb : DbContext
    {
        public DbSet<Test> Tests { get; set; }
 
        protected override  void OnConfiguring(DbContextOptionsBuilder builder)
        {
            builder.UseSqlServer("Server=.\\Sistemas,1433;Database=Teste_Functions;Integrated Security=True;");
        }
 
        protected override  void OnModelCreating(ModelBuilder builder)
        {
            // Traduzir DATEDIFF
            builder
                .HasDbFunction(typeof(Funcoes)
                .GetMethod(nameof(Funcoes.SqlDateDiff)))
                .HasTranslation(args =>
                {
                    var argumentos = args.ToList();
                    argumentos[0] = new  SqlFragmentExpression((string)((ConstantExpression)argumentos.First()).Value);
                    return new  SqlFunctionExpression(
                        "DATEDIFF",
                        typeof(int),
                        argumentos);
                });
 
            // Traduzir LEFT
            builder
                .HasDbFunction(typeof(Funcoes)
                .GetMethod(nameof(Funcoes.SqlLeft)))
                .HasTranslation(args =>
                {
                    var argumentos = args.ToList();
                    return new  SqlFunctionExpression(
                        "LEFT",
                        typeof(string),
                        argumentos);
                });
 
            // Traduzir REPLACE
            builder
                .HasDbFunction(typeof(Funcoes)
                .GetMethod(nameof(Funcoes.SqlReplace)))
                .HasTranslation(args =>
                {
                    var argumentos = args.ToList();
                    return new  SqlFunctionExpression(
                        "REPLACE",
                        typeof(string),
                        argumentos);
                });
 
            base.OnModelCreating(builder);
        }
    }
 
    public class  Funcoes
    {
        public static  int SqlDateDiff(string tipo, DateTime inicio, DateTime fim)
            => 0;
 
        public static  string SqlLeft(object dados, int limite)
            => string.Empty;
 
        public static  string SqlReplace(object dados, string substituir, string por)
            => string.Empty;
    }
 
    public class  Test
    {
        public int  Id { get; set; }
        public string  Nome { get; set; }
        public DateTime Data { get; set; }
    }
}

Fico por aqui, abraços!