TargetInvocationException when returning a null enumerable
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 505
- Forks
- 186
- PR merge metrics
- No merged PRs in 30d
Description
Assemblies affected
ASP.NET Core OData 8.0.11
Describe the bug
If we return a model which has an enumerable property for example an IEnumerable with null value a TargetInvokationException is thrown, I see that this happens under specific conditions, if any of the conditions is not met, the issue doesn't happen
- The endpoint has a PageSize defined
- We are using query parameters to select the IEnumerable property which value is null
Reproduce steps
namespace Issue
{
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.OData;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
public class WeatherForecast
{
[Key]
public Guid Key { get; set; }
public IEnumerable<string> Additional => null;
}
public class WeatherForecastController : ODataController
{
[EnableQuery(PageSize = 4)]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Key = Guid.NewGuid(),
});
}
}
public class Program
{
public static void Main(string[] args)
{
IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
var entity = builder.EntitySet<WeatherForecast>("WeatherForecast");
return builder.GetEdmModel();
}
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers().AddOData(opt =>
{
opt.AddRouteComponents("odata", GetEdmModel()).Select().Expand().OrderBy().SetMaxTop(10).Count().Filter().SkipToken();
});
var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
app.Run();
}
}
}
Request/Response
Query http://localhost:54321/odata/WeatherForecast and we can see the Additional property is serialized as an empty array which I guess is the correct behavior
{
"@odata.context": "http://localhost:54321/odata/$metadata#WeatherForecast",
"value": [
{
"Key": "10fcb5d4-7558-4527-a88d-59ebd7ed18be",
"Additional": []
},
{
"Key": "55542ada-9a3a-4e28-a338-7b2908360654",
"Additional": []
},
{
"Key": "a57bb67c-ce54-4b1e-b51e-b929068e9371",
"Additional": []
},
{
"Key": "b035b3f9-244b-4251-9d4a-104ce0c26b68",
"Additional": []
}
],
"@odata.nextLink": "http://localhost:54321/odata/WeatherForecast?$skiptoken=Key-b035b3f9-244b-4251-9d4a-104ce0c26b68"
}
But if we make a query to http://localhost:54321/odata/WeatherForecast?$select=Key,Additional a System.Reflection.TargetInvocationException is thrown
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'source')
at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
at lambda_method15(Closure , WeatherForecast )
at System.Linq.Enumerable.SelectIPartitionIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at Microsoft.AspNetCore.OData.Query.Container.TruncatedCollection`1..ctor(IQueryable`1 source, Int32 pageSize, Boolean parameterize)
at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.LimitResults[T](IQueryable`1 queryable, Int32 limit, Boolean parameterize, Boolean& resultsLimited)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.LimitResults(IQueryable queryable, Int32 limit, Boolean parameterize, Boolean& resultsLimited)
at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ApplyPaging(IQueryable result, ODataQuerySettings querySettings)
at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ApplyTo(IQueryable query, ODataQuerySettings querySettings)
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.ExecuteQuery(Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext, Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext)
at Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
If we have the attribute like [EnableQuery] instead of [EnableQuery(PageSize = 4)] the issue doesn't happen
Expected behavior
Should be consistent and not throw the exception in that specific case
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the reproduction with ASP.NET Core OData 8.0.11 and the $select=Key,Additional query. Read ODataQueryOptions.LimitResults, TruncatedCollection, and EnableQueryAttribute around the paging stack shown in the exception. Done means the paged endpoint handles a null enumerable consistently with the unpaged endpoint without throwing TargetInvocationException.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100