-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
DbFunction don't get the mapping function when an interface is used
There's a strange behavior when an interface is used instead of the direct object of dbcontext when a DbFunciton is called. In the code bellow, I create a function in sql (Test) and added it to my context using DbFunctionAttribute.
If MyDbContext is used to call the Test function works ok. The problem is when the IMyDbContext interface is required to the dependency injection, the Test function throws the NotSupportedException like the mapping doesn't exist.
I tried to cast the interface reference to the base class and works ok, but I believe it's not the desired behavior using interfaces. Also, I tried to add virtual to Test function in MyDbContext, but didn't works with the interface.
Code
CREATE FUNCTION dbo.Test(@i int) RETURNS int AS
begin
return @i;
endpublic interface IMyDbContext {
int Test(int i);
}
public class MyDbContext(DbContextOptions<MyDbContext> dbContextOptions) : DbContext(dbContextOptions), IMyDbContext {
...
[DbFunction("Test", "dbo", IsBuiltIn = false)]
public int Test(int i) => throw new NotSupportedException();
}
// Added both to my dependency injection
var builder = WebApplication.CreateBuilder(args);
builder
.AddDbContext<IMyDbContext, MyDbContext>(ServiceLifetime.Transient)
.AddDbContext<MyDbContext>(ServiceLifetime.Transient);public class MyController(IMyDbContext iContext, MyDbContext context): ControllerBase
{
public IActionResult TestOk() => Ok(
context.Users.Select(it => context.Test(it.Id))); // using concrete class
public IActionResult TestWrong() => Ok(
iContext .Users.Select(it => iContext .Test(it.Id))); // using interface
public virtual IActionResult TestCastOk()
{
var ctx = iContext as MyDbContext; // using interface, casted to concrete class
return Ok(ctx!.Users.Select(it => ctx.Tomorrow(it.Id)));
}
}Stack traces
[12:43:18 DBG] Compiling query expression:
'DbSet<User>()
.Select(it => __P_0.Test(it.Id))'
[12:43:18 DBG] Generated query execution expression:
'queryContext => new SingleQueryingEnumerable<int>(
(RelationalQueryContext)queryContext,
RelationalCommandCache.QueryExpression(
Client Projections:
0 -> 0
SELECT u.Id
FROM Users.Users AS u),
null,
Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, int>,
MyDbContext,
False,
True,
True
)'
[12:43:18 DBG] Creating DbConnection.
[12:43:18 DBG] Created DbConnection. (2ms).
[12:43:18 DBG] Opening connection to database '' on server ''.
[12:43:18 DBG] Opened connection to database '' on server ''.
[12:43:18 DBG] Creating DbCommand for 'ExecuteReader'.
[12:43:18 DBG] Created DbCommand for 'ExecuteReader' (1ms).
[12:43:18 DBG] Initialized DbCommand for 'ExecuteReader' (3ms).
[12:43:18 DBG] Executing DbCommand [Parameters=[], CommandType='"Text"', CommandTimeout='30']
SELECT [u].[Id]
FROM [Users].[Users] AS [u]
[12:43:18 INF] Executed DbCommand (3ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
SELECT [u].[Id]
FROM [Users].[Users] AS [u]
[12:43:18 ERR]
An exception occurred while iterating over the results of a query for context type 'MyDbContext'.
System.NotSupportedException.
at MyDbContext.Test(Int32 i) in MyDbContext.Accounting
at lambda_method39(Closure, QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
[12:43:18 DBG] Closing data reader to '' on server ''.
[12:43:18 DBG] A data reader for '' on server '' is being disposed after spending 113ms reading results.
Include provider and version information
EF Core version: Microsoft.EntityFrameworkCore, 8.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer, 8.0.0
Target framework: net8.0
Operating system: Windows 10
IDE: Microsoft Visual Studio Professional 2022 (64-bit) 17.8.3