简体   繁体   中英

Mount SQL query to EXTRACT postgresql function with EF Core 3.1

I'm using Postgresql with EF Core 3.1 on my ASP.NET project

I want to generate a sql fragment like this:

EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE f."MyDateTimeField")"

So, I have writed this code on OnModelCreating method of the DbContext:

    modelBuilder
                .HasDbFunction(typeof(MyDbFunctions).GetMethod(
                    nameof(MyDbFunctions.ExtractEpochFromTimestampWithTimezone)))
                .HasTranslation(args
                    => SqlFunctionExpression.Create(
                        "EXTRACT",
                        new []
                        {
                            new SqlFragmentExpression(
                                $"EPOCH FROM TIMESTAMP WITH TIME ZONE"),
                            args.First()
                        }, typeof(double), null));

But the generated SQL fragment was:

EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE, f."MyDateTimeField")

Note that SQL has one "," after word "ZONE".

How can I fix my code to generate the SQL fragment without this ","?

Any one that faced same issues as above. I have solve above issue with the following code

Test entity

public class Employee
{
    public int Id { get; set; }
    public DateTime DOB { get; set; }
    public string Name { get; set; }

}

EF Core Dbcontext with custom postgres EXTRACT function

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
        modelBuilder.HasDbFunction(ExtractMethod)
            .HasTranslation(args =>
            {
                var arguments = args.ToList();
                //{((Microsoft.EntityFrameworkCore.Query.SqlExpressions.ColumnExpression)arguments[1]).TypeMapping.StoreType}
                arguments[0] = new SqlFragmentExpression($"{((Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlConstantExpression)arguments[0]).Value} from {((Microsoft.EntityFrameworkCore.Query.SqlExpressions.ColumnExpression)arguments[1]).Table.Alias}.\"{((Microsoft.EntityFrameworkCore.Query.SqlExpressions.ColumnExpression)arguments[1]).Name}\"");
                arguments.RemoveAt(1);
                return new SqlFunctionExpression("EXTRACT",
                   arguments,
                   false,
                   new bool[] { false, false},
                   typeof(decimal),
                   RelationalTypeMapping.NullMapping);
            });
}

EF function which is mapped

private readonly MethodInfo ExtractMethod
    = typeof(Context).GetRuntimeMethod(nameof(Extract), new[] { typeof(string), typeof(DateTime) });
public DateTime Extract(string field, DateTime source)
            => throw new NotSupportedException();

UPDATED CODE

The above method will fail when using nested functions. I have updated it with proper implementaion. I hope this will help others

Added a new class called ExtractExpression

 public class ExtractExpression:SqlFunctionExpression
{
    private readonly IReadOnlyCollection<SqlExpression> _params;        

    public ExtractExpression(IReadOnlyCollection<SqlExpression> parameters):base("EXTRACT", true, typeof(double), RelationalTypeMapping.NullMapping)
    {
        _params = parameters;
    }        
    protected override Expression Accept(ExpressionVisitor visitor)
    {
        if (!(visitor is QuerySqlGenerator))
            return base.Accept(visitor);

        visitor.Visit(new SqlFragmentExpression("EXTRACT("));   //Postgres function name
        visitor.Visit(_params.First());                         //First paramenter
        visitor.Visit(new SqlFragmentExpression(" from "));     //query clause
        visitor.Visit(_params.Skip(1).First());                 //2nd parameter
        visitor.Visit(new SqlFragmentExpression(")"));          //function ending brace

        return this;
    }
    protected override void Print([NotNullAttribute] ExpressionPrinter expressionPrinter)
    {
        Console.WriteLine(expressionPrinter);
    }
}

Add below code in OnModeCreating function

 modelBuilder.HasDbFunction(ExtractMethod)
                 .HasTranslation(expressions =>
                 {
                     return new ExtractExpression(expressions);
                 });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM