Skip to content

How to write DbFunction's translation? #11295

@Kation

Description

@Kation

When we use ModelBuilder.HasDbFunction to add a database function to model
It generate the SQL with QUOTE char all of the arguments
With SQL Server 2016, there is a JSON_VALUE function
It's require first argument to be column without QUOTE, and second argument not be a parameter.

I wrote a test to solve first question but a bug exist.

Write a static method

public static class JsonExtensions
{
    public static string JsonValue(string column, string path)
    {
        throw new NotSupportedException();
    }
}

Write a expression translate method

public static class JsonExpressionTranslator
{
    public static Expression Translate(IReadOnlyCollection<Expression> expressions)
    {
        var items = expressions.ToArray();
        return new JsonExpression(items[0], items[1]);
    }
}

Write a json expression

public class JsonExpression : Expression
{
    public JsonExpression(Expression column, Expression path)
    {
        Column = column;
        Path = path;
    }

    public override ExpressionType NodeType => ExpressionType.Extension;

    public Expression Column { get; private set; }

    public Expression Path { get; private set; }

    protected override Expression Accept(ExpressionVisitor visitor)
    {
        if (visitor is ISqlExpressionVisitor specificVisitor)
        {
            string sql = $"JSON_VALUE([{Column.ToString().Trim('"')}],";
            specificVisitor.VisitSqlFragment(new SqlFragmentExpression(sql));
            visitor.Visit(Path);
            sql = ")";
            return specificVisitor.VisitSqlFragment(new SqlFragmentExpression(sql));
        }
        else
            return base.Accept(visitor);
    }

    protected override Expression VisitChildren(ExpressionVisitor visitor)
    {
        return new JsonExpression(visitor.Visit(Column), visitor.Visit(Path));
    }

    public override Type Type => typeof(string);

    public override string ToString() => $"JSON_VALUE([{Column.ToString().Trim('"')}], '{Path.ToString().Trim('"')}')";
}

Configure model

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasDbFunction(typeof(JsonExtensions).GetMethod("JsonValue"), options => options.HasTranslation(JsonExpressionTranslator.Translate));
}

Write query

var path = "$.Filter"; //this argument used to be dynamic
var result = dbContext.Items.Where(t => JsonExtensions.JsonValue("Values", path).ToArray();

It came out sql with
JSON_VALUE([Values],@__path_1)
And column name has no table relate, path is a parameter

-- How to generate sql without QUOTE?
-- How to generate sql that argument is not a PARAMETER?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions