terrajobst / nquery-vnext

A Roslyn inspired rewrite of NQuery
MIT License
72 stars 16 forks source link

ArgumentException when trying to call a function: "Static method requires null instance, non-static method requires non-null instance." #67

Open dallmair opened 1 month ago

dallmair commented 1 month ago

We're trying to extend NQuery's default DataContext with custom instance functions. Rough sketch of the implementation (intentionally not using a lambda here to not take a dependency on C# compiler transformations):

internal sealed class Something : ISomething
{
    private readonly ISomeService _someService;
    private readonly DataContext _dataContext;

    public Something(ISomeService someService)
    {
        _someService = someService;
        _dataContext = DataContext.Default.WithFunctions(
            DataContext.Default.Functions.Concat(GetCustomFunctions())
        );
    }

    private IEnumerable<FunctionSymbol> GetCustomFunctions()
    {
        yield return new FunctionSymbol<DateTime, DateTime>("DoSomething", "value", DoSomething);
    }

    private DateTime DoSomething(DateTime dateTime)
    {
        return _someService.DoSomething(dateTime);
    }

The creation of the DataContext works fine, but when we want to use the function in a query:

SELECT  DoSomething(GETDATE())

We're getting an exception (copied from VS test log):

  Message: 
    System.ArgumentException : Static method requires null instance, non-static method requires non-null instance.

  Stack Trace: 
    Expression.ValidateStaticOrInstanceMethod(Expression instance, MethodInfo method)
    Expression.ValidateMethodAndGetParameters(Expression instance, MethodInfo method)
    Expression.Call(Expression instance, MethodInfo method)
    Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
    FunctionSymbol`1.CreateInvocation(IEnumerable`1 arguments) line 36
    ExpressionBuilder.BuildInvocation(FunctionSymbol functionSymbol, IEnumerable`1 arguments) line 145
    ExpressionBuilder.BuildFunctionInvocationExpression(BoundFunctionInvocationExpression expression) line 404
    ExpressionBuilder.BuildExpression(BoundExpression expression) line 196
    ExpressionBuilder.BuildCachedExpression(BoundExpression expression) line 71
    ExpressionBuilder.BuildExpression[TDelegate](BoundExpression expression, Type targetType) line 170
    ExpressionBuilder.BuildExpression[TDelegate](BoundExpression expression, Type targetType, RowBufferAllocation allocation) line 66
    ExpressionBuilder.BuildIteratorFunction(BoundExpression expression, RowBufferAllocation allocation) line 52
    IteratorBuilder.BuildFunction(BoundExpression expression, RowBufferAllocation allocation) line 29
    <>c__DisplayClass14_0.<BuildCompute>b__0(BoundComputedValue dv) line 170
    SelectArrayIterator`2.Fill(ReadOnlySpan`1 source, Span`1 destination, Func`2 func)
    SelectArrayIterator`2.ToArray()
    Enumerable.ToArray[TSource](IEnumerable`1 source)
    ImmutableArray.CreateRange[T](IEnumerable`1 items)
    ImmutableArray.ToImmutableArray[TSource](IEnumerable`1 items)
    IteratorBuilder.BuildCompute(BoundComputeRelation relation) line 169
    IteratorBuilder.BuildRelation(BoundRelation relation) line 54
    IteratorBuilder.BuildProject(BoundProjectRelation relation) line 241
    IteratorBuilder.BuildRelation(BoundRelation relation) line 64
    IteratorBuilder.Build(BoundRelation relation) line 19
    CompiledQuery.CreateReader(Boolean schemaOnly) line 30
    CompiledQuery.CreateReader() line 19
    Query.ExecuteReader() line 47