Skip to content

Commit

Permalink
Custom method can now have nullable parameters and nullable arguments…
Browse files Browse the repository at this point in the history
… for 9.x (#1374)

* Custom method can now have nullable parameters and nullable arguments

* added unit tests for MakeCustomFunctionCall

* remove unused imports

* align code
  • Loading branch information
bjelbo authored Jan 9, 2025
1 parent e402fa7 commit da41295
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ private Expression BindCustomMethodExpressionOrNull(SingleValueFunctionCallNode
MethodInfo methodInfo;
if (UriFunctionsBinder.TryGetMethodInfo(node.Name, methodArgumentsType, out methodInfo))
{
return ExpressionBinderHelper.MakeFunctionCall(methodInfo, QuerySettings, arguments);
return ExpressionBinderHelper.MakeCustomFunctionCall(methodInfo, arguments);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,22 @@ public static Expression MakeFunctionCall(MemberInfo member, ODataQuerySettings
return CreateFunctionCallWithNullPropagation(functionCall, arguments, querySettings);
}

//Custom methods might contain nullable parameters and, therefore, also should be able to take arguments of type Nullable<T>
public static Expression MakeCustomFunctionCall(MethodInfo method, params Expression[] arguments)
{
Expression functionCall;
if (method.IsStatic)
{
functionCall = Expression.Call(null, method, arguments);
}
else
{
functionCall = Expression.Call(arguments.First(), method, arguments.Skip(1));
}

return functionCall;
}

public static Expression CreateFunctionCallWithNullPropagation(Expression functionCall, Expression[] arguments, ODataQuerySettings querySettings)
{
if (querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ protected virtual Expression BindCustomMethodExpressionOrNull(SingleValueFunctio
MethodInfo methodInfo;
if (UriFunctionsBinder.TryGetMethodInfo(node.Name, methodArgumentsType, out methodInfo))
{
return ExpressionBinderHelper.MakeFunctionCall(methodInfo, context.QuerySettings, arguments);
return ExpressionBinderHelper.MakeCustomFunctionCall(methodInfo, arguments);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Moq;
using Xunit;
using Microsoft.AspNetCore.OData.Edm;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Query.Expressions;
using Microsoft.AspNetCore.OData.Query.Wrapper;
using Microsoft.AspNetCore.OData.TestCommon;
using Microsoft.AspNetCore.OData.Tests.Commons;
using Microsoft.AspNetCore.OData.Tests.Models;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.UriParser;
using Microsoft.AspNetCore.OData.Tests.Models;
using Microsoft.AspNetCore.OData.TestCommon;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Xunit;

namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions;

Expand Down Expand Up @@ -493,6 +494,56 @@ private static IEdmModel BuildAndGetEdmModel()
return builder.GetEdmModel();
}
#endregion


[Theory]
[InlineData(0)]
[InlineData(null)]
public void MakeCustomFunctionCall_StaticMethod_ShouldCreateCorrectExpression(int? value)
{
// Arrange
MethodInfo methodInfo = typeof(TestCustomFunctionCall).GetMethod(nameof(TestCustomFunctionCall.StaticCustomMethod));
Expression[] arguments = { Expression.Constant(value, typeof(int?)) };

// Act
Expression result = ExpressionBinderHelper.MakeCustomFunctionCall(methodInfo, arguments);

// Assert
Assert.NotNull(result);
Assert.IsAssignableFrom<MethodCallExpression>(result);
var methodCall = (MethodCallExpression)result;
Assert.Equal(methodInfo, methodCall.Method);
Assert.Equal(arguments, methodCall.Arguments);
}

[Theory]
[InlineData(0)]
[InlineData(null)]
public void MakeCustomFunctionCall_InstanceMethod_ShouldCreateCorrectExpression(int? value)
{
// Arrange
MethodInfo methodInfo = typeof(TestCustomFunctionCall).GetMethod(nameof(TestCustomFunctionCall.InstanceCustomMethod));
Expression instance = Expression.Constant(new TestCustomFunctionCall());
Expression[] arguments = { instance, Expression.Constant(value, typeof(int?)) };

// Act
Expression result = ExpressionBinderHelper.MakeCustomFunctionCall(methodInfo, arguments);

// Assert
Assert.NotNull(result);
Assert.IsAssignableFrom<MethodCallExpression>(result);
var methodCall = (MethodCallExpression)result;
Assert.Equal(methodInfo, methodCall.Method);
Assert.Equal(arguments.Skip(1), methodCall.Arguments);
Assert.Equal(instance, methodCall.Object);
}
}


internal class TestCustomFunctionCall
{
public static void StaticCustomMethod(int? x) { }
public void InstanceCustomMethod(int? x) { }
}

public class MyQueryBinder : QueryBinder
Expand Down

0 comments on commit da41295

Please sign in to comment.