Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Oracle built in functions in Entity Framework Core?

With EF 6 (and .NET Framework), I was able to map Oracle built in functions by creating a static class like this:

public static class OracleFunctions
{
    [Function(FunctionType.BuiltInFunction, "TO_CHAR")]
    public static string ToChar(this decimal? value) => Function.CallNotSupported<string>();
   
    [Function(FunctionType.BuiltInFunction, "LENGTHB")]
    public static int? LENGTHB(this byte[] value) => Function.CallNotSupported<int?>();
}

And then I added this code to OnModelCreating:

modelBuilder.Conventions.Add(new FunctionConvention(typeof(OracleFunctions)));

However, this syntax does not work for EF Core. I attempted to create a similar static class:

public static class OracleFunctions
{
    public static int LENGTHB(this byte[] input)
    {
        throw new NotSupportedException("This function can only be used in LINQ to Entities queries.");
    }

    public static string ToChar(this decimal? value)
    {
        throw new NotSupportedException("This function can only be used in LINQ to Entities queries.");
    }        
}

And added them in OnModelCreating:

modelBuilder.HasDbFunction(typeof(OracleFunctions).GetMethod(nameof(OracleFunctions.LENGTHB)))
            .HasName("LENGTHB");

modelBuilder.HasDbFunction(typeof(OracleFunctions).GetMethod(nameof(OracleFunctions.ToChar)))
            .HasName("TO_CHAR");

Using the functions like this:

var bar = dbContext.WORKFLOW_DOCUMENT.Where(x => OracleFunctions.LENGTHB(x.Template) > 0).FirstOrDefault();

But it results in the following error:

Oracle.ManagedDataAccess.Client.OracleException: 'ORA-00904: "MySchema"."LENGTHB": invalid identifier https://docs.oracle.com/error-help/db/ora-00904/'

like image 759
sstr27 Avatar asked Jan 25 '26 12:01

sstr27


2 Answers

As can be seen in the Oracle error message, the schema name MySchema is being applied to the function name LENGTHB. Removing the following from OnModelCreating will prevent the schema name from being added.

modelBuilder.HasDefaultSchema("MySchema")

With this change, the LENGTHB function, as defined in the original question, now works. The TO_CHAR function now throws a different error. However it may not be needed as there appears to be better .ToString() handling with EF Core and the related Oracle packages. For example, I have not encountered the dreaded

ORA-00932 inconsistent datatypes: expected CHAR got NCLOB

yet when using .ToString() on numeric fields.

A streamlined alternative to defining built in functions (as can be seen here) is adding a static function to your database context:

[DbFunction("LENGTHB")]
public static int? LengthB(byte[] input) => throw new NotSupportedException("This function can only be used in LINQ to Entities queries.");

This can then be called like:

var foo = dbContext.WORKFLOW_DOCUMENT.Where(x => MyTestContext.LENGTHB(x.Template) > 0).FirstOrDefault();
like image 177
sstr27 Avatar answered Jan 27 '26 00:01

sstr27


Schema doesn't matter for built-in functions, but you have to tell EF that your function is built-in (by default it is assumed to be user-defined). You do that by ... well, using the surprisingly named IsBuiltIn method of the DbFunctionBuilder class returned by the all HasDbFunction fluent API. e.g.

modelBuilder.HasDbFunction(typeof(OracleFunctions).GetMethod(nameof(OracleFunctions.LENGTHB)))
    .HasName("LENGTHB")
    .IsBuiltIn(); // <--

As you can see, the class has many other methods (HasSchema, HasParameter, HasStoreType, IsNullable and even HasTranslation) which allow you to configure all aspects of the function mapping, not just the name.

DbFunction attribute also has some properties (IsBuiltIn, IsNullable, Name, Schema), but is not so flexible as the fluent API.

like image 37
Ivan Stoev Avatar answered Jan 27 '26 01:01

Ivan Stoev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!