Banco de Dados

25 jan, 2018

Traduzindo funções com Entity Framework Core

Publicidade

Uma das várias novidades que surgiram no Entity Framework Core 2.0, foi o suporte para UDF: a utilização das funções pré-definas 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, te mostrarei 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 (isso pode ser possível também, mas aqui irei apenas demonstrar como traduzir isso para o servidor).

A 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;
}

Iremos criar nossas expressões no método OnModelCreating 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]

Todos os códigos utilizados em nossos exemplos estão reunidos 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, e um forte abraço!

Fonte original em: