Microsoft.EntityFrameworkCore 8.0.2: Inferring type mapping based on operand size has caused regression for the Oracle EF core provider
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
With reference to the below comment on #32520
> @indrajitjadeja the fix in https://github.com/dotnet/efcore/pull/32510 only corrected the length on the type mapping; this is a general type mapping inference fix that isn't SQL Server-specific (even if the original bug manifested specifically in SQL Server). I'm still not clear on how it exactly it affects the Oracle SQL translation - can you please open a new issue with a clear minimal, runnable repro and the SQL generated in 8.0.1 and 8.0.2?
### Minimal repro:
```C#
using var context = new BlogContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.Persons.Add(new() { ThreeCharacterProperty = "AAA", FiveCharacterProperty = "BBBBB" });
context.SaveChanges();
var queryData = new[] { "AAA;BBBBB", "AAA;CCCCC" };
Console.WriteLine(context.Persons.Count(x => queryData.Contains(x.ThreeCharacterProperty + ";" + x.FiveCharacterProperty)));
public class BlogContext : DbContext
{
public DbSet Persons { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseOracle(@"User Id=scott;Password=tiger;Data Source=inst1")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}
public class Person
{
public int Id { get; set; }
[Column(TypeName = "char(3)")]
public string ThreeCharacterProperty { get; set; }
[Column(TypeName = "char(5)")]
public string FiveCharacterProperty { get; set; }
}
```
Application using Oracle.EntityFrameworkCore **8.21.121** with Oracle.EntityFrameworkCore **8.0.1** generates the following SQL without any error:
```SQL
SELECT COUNT(*)
FROM "Persons" "p"
WHERE (COALESCE("p"."ThreeCharacterProperty", NULL) || ';') || COALESCE("p"."FiveCharacterProperty", NULL) IN ('AAA;BBBBB', 'AAA;CCCCC')
```
Application using Oracle.EntityFrameworkCore **8.21.121** with Oracle.EntityFrameworkCore **8.0.2** generates the following SQL:
```SQL
SELECT COUNT(*)
FROM "Persons" "p"
WHERE (COALESCE("p"."ThreeCharacterProperty", NULL) || N';') || COALESCE("p"."FiveCharacterProperty", TO_NCLOB(N'')) IN (TO_NCLOB(N'AAA;BBBBB'), TO_NCLOB(N'AAA;CCCCC'))
```
which throws the below error:
```
Unhandled exception. Oracle.ManagedDataAccess.Client.OracleException (0x80004005): ORA-00932: inconsistent datatypes: expected CHAR got NCLOB
at OracleInternal.ServiceObjects.OracleConnectionImpl.VerifyExecution(Int32& cursorId, Boolean bThrowArrayBindRelatedErrors, SqlStatementType sqlStatementType, Int32 arrayBindCount, OracleException& exceptionForArrayBindDML, Boolean& hasMoreRowsInDB, Boolean bFirstIterationDone)
at OracleInternal.ServiceObjects.OracleCommandImpl.ExecuteReader(String commandText, OracleParameterCollection paramColl, CommandType commandType, OracleConnectionImpl connectionImpl, OracleDataReaderImpl& rdrImpl, Int32 longFetchSize, Int64 clientInitialLOBFS, OracleDependencyImpl orclDependencyImpl, Int64[] scnForExecution, Int64[]& scnFromExecution, OracleParameterCollection& bindByPositionParamColl, Boolean& bBindParamPresent, Int64& internalInitialLOBFS, Int64 internalInitialJSONFS, OracleException& exceptionForArrayBindDML, OracleConnection connection, IEnumerable`1 adrianParsedStmt, Boolean isDescribeOnly, Boolean isFromEF)
at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior)
at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior)
at Oracle.EntityFrameworkCore.Storage.Internal.OracleRelationalCommandBuilderFactory.OracleRelationalCommandBuilder.OracleRelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.<>c.b__21_0(DbContext _, Enumerator enumerator)
at Oracle.EntityFrameworkCore.Storage.Internal.OracleExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Boolean& found)
at lambda_method45(Closure, QueryContext)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at ConsoleApp1.Program.Main() in C:\ConsoleApp1\ConsoleApp1\Program.cs:line 39
```
### Debug information
In the oracle provider, depending on the size parameter, a string CLR type maps to either VARCHAR2/NVARCHAR2 or CLOB/NCLOB store types.
out of the whole binary expression:
```c#
x.ThreeCharacterProperty + ";" + x.FiveCharacterProperty
```
when the left part of the binary expression:
```c#
/*left*/ x.ThreeCharacterProperty /*operand */ + /*right*/ ";"
```
requires inferring of type mapping, the right SqlConstantExpression does not have a TypeMapping thus the value of [inferredSize](https://github.com/dotnet/efcore/commit/fed6fe113699c923242f4751eee9dcdf60d523d9#diff-cc71b50a38a9a9e0892ab9c10edc4308f7d2f608fc36a4ef1be36c3d444d5e34R223) becomes null and it calls [_typeMappingSource.FindMapping](https://github.com/dotnet/efcore/commit/fed6fe113699c923242f4751eee9dcdf60d523d9#diff-cc71b50a38a9a9e0892ab9c10edc4308f7d2f608fc36a4ef1be36c3d444d5e34R242) on string CLR type with Unicode and Oracle provider returns NVARCHAR2 as type mapping with 2000 default size.
now when the right part of the binary expression gets evaluated:
```c#
/*left*/ x.ThreeCharacterProperty + ";" /*operand */ + /*right*/ x.FiveCharacterProperty
```
the left Sql Binary Expression part already has 2000 as a size and the right SqlColumnEpxression has 5 as a size which sets the [inferredSize](https://github.com/dotnet/efcore/commit/fed6fe113699c923242f4751eee9dcdf60d523d9#diff-cc71b50a38a9a9e0892ab9c10edc4308f7d2f608fc36a4ef1be36c3d444d5e34R223) value to 2005 and it calls [_typeMappingSource.FindMapping](https://github.com/dotnet/efcore/commit/fed6fe113699c923242f4751eee9dcdf60d523d9#diff-cc71b50a38a9a9e0892ab9c10edc4308f7d2f608fc36a4ef1be36c3d444d5e34R242) on String CLR type with Unicode and Oracle provider returns NCLOB as type mapping.
In the case of the Oracle provider with string CLR type, an additional call to the [_typeMappingSource.FindMapping](https://github.com/dotnet/efcore/commit/fed6fe113699c923242f4751eee9dcdf60d523d9#diff-cc71b50a38a9a9e0892ab9c10edc4308f7d2f608fc36a4ef1be36c3d444d5e34R242) method based on the inferred size results in incorrect type mapping combinations, despite the fact that each SqlColumnEpxression has already been assessed with the correct type mapping.
### provider and version information
EF Core version: 8.0.2
Database provider: Oracle.EntityFrameworkCore 8.21.121
Target framework: .NET 8.0
Operating system: Windows 11
IDE: Visual Studio 2022 17.7.4
Contributor guide
Assessment
This issue has not been assessed yet.