Skip to content

OData queries don't work #525

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ghost opened this issue Aug 5, 2019 · 9 comments
Closed

OData queries don't work #525

ghost opened this issue Aug 5, 2019 · 9 comments

Comments

@ghost
Copy link

ghost commented Aug 5, 2019

I have added a versioning in WebApi project. If I use OData queries such like this:
/api/v1/Object?$skip=1
the status 500 is returned with message:

No service for type 'Microsoft.AspNet.OData.Query.SkipTokenHandler' has been registered.

In WebApiConfig.cs I have added the following code:
config.AddApiVersioning();
builder = new VersionedODataModelBuilder(config)
{
ModelConfigurations = { new ObjectModelConfiguration() }
};
config.MapVersionedODataRoutes("odata", "api/v{apiVersion}", builder.GetEdmModels());

Before adding Microsoft.AspNet.OData.Versioning OData queries were working.

@ghost ghost changed the title OData queries don't work OData queries doesn't work Aug 5, 2019
@ghost ghost changed the title OData queries doesn't work OData queries don't work Aug 5, 2019
@commonsensesoftware commonsensesoftware self-assigned this Aug 5, 2019
@commonsensesoftware
Copy link
Collaborator

Did that also require you to update the version of the OData library you've been using? There are a number of behavioral, breaking changes between major versions. You might need to share more about one of your controller setups. I was able to take the basic OData example for Web API and have $skip and $top work without any configuration changes; though I did have to change it just a bit to return multiple results. For example:

http://localhost/api/v1/people?$top=1&$skip=0

Queries definitely work, but I'll need some more information about your configuration to help you get things working.

@ghost
Copy link
Author

ghost commented Aug 6, 2019

I'm using latest versions of NuGet libraries:

  • Microsoft.AspNet.Mvc 5.2.7
  • Microsoft.AspNet.OData 7.2.0
  • Microsoft.AspNet.OData.Versioning 3.0.1
  • Microsoft.AspNet.WebApi 5.2.7
  • Microsoft.AspNet.WebApi.Client 5.2.7
  • Microsoft.AspNet.WebApi.Core 5.2.7
  • Microsoft.AspNet.WebApi.HelpPage 5.2.7
  • Microsoft.AspNet.WebApi.Versioning 3.0.2
  • Microsoft.AspNet.WebApi.WebHost 5.2.7
  • Microsoft.AspNet.WebPages 3.2.7
  • Microsoft.Extensions.DependencyInjection 2.2.0
  • Microsoft.Extensions.DependencyInjection.Abstractions 2.2.0
  • Microsoft.OData.Core 7.6.0
  • Microsoft.OData.Edm 7.6.0
  • Microsoft.Spatial 7.6.0
    and some other, .NET Framework 4.8
    web.config is by default, only connection strings added

@ghost
Copy link
Author

ghost commented Aug 6, 2019

full WebApiConfig.cs:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.AddApiVersioning(options =>
        {
            options.Conventions.Add(new VersionByNamespaceConvention());
        });

        // Конфигурация и службы веб-API
        VersionedODataModelBuilder builder = new VersionedODataModelBuilder(config)
        {
            ModelConfigurations = { new ObjectModelConfiguration() }
        };
        config.MapVersionedODataRoutes("odata", "api/v{apiVersion}", builder.GetEdmModels());

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

@ghost
Copy link
Author

ghost commented Aug 6, 2019

ObjectController.cs:

[ApiExplorerSettings(IgnoreApi = false)]
[EnableQuery]
public class ObjectController : VersionedMetadataController
{
    public IQueryable<Object> Get()
    {
        return Objects.GetByUserQueryable(GetCurrentUser(), (DbContext)context);
    }
}

@ghost
Copy link
Author

ghost commented Aug 6, 2019

exception info:
{
"code": "",
"message": "Произошла ошибка.",
"innererror": {
"message": "The "ObjectContent1\" type failed to serialize the response body for content type \"application/json; odata.metadata=minimal\".", "type": "System.InvalidOperationException", "stacktrace": "", "internalexception": { "message": "No service for type 'Microsoft.AspNet.OData.Query.SkipTokenHandler' has been registered.", "type": "System.InvalidOperationException", "stacktrace": " в Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)\r\n в Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)\r\n в Microsoft.AspNet.OData.ODataQueryContextExtensions.GetSkipTokenHandler(ODataQueryContext context)\r\n в Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.GetNextLinkGenerator(ODataResourceSetBase resourceSet, IEnumerable resourceSetInstance, ODataSerializerContext writeContext)\r\n в Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSet(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext)\r\n в Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)\r\n в Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)\r\n в Microsoft.AspNet.OData.Formatter.ODataOutputFormatterHelper.WriteToStream(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func2 getODataMessageWrapper, Func2 getEdmTypeSerializer, Func2 getODataPayloadSerializer, Func`1 getODataSerializerContext)\r\n в Microsoft.AspNet.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- Конец трассировка стека из предыдущего расположения, где возникло исключение ---\r\n в System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n в System.Web.Http.WebHost.HttpControllerHandler.d__22.MoveNext()"
}
}
}

@commonsensesoftware
Copy link
Collaborator

Спасибо

In short, OData 7.2 introduces a behavioral change that breaks API Versioning. This is because OData changed the service collection - again. You can see this in ContainerBuilderExtensions.AddDefaultWebApiServices. Specifically, the SkipTokenHandler and SkipTokenQueryValidator are new types that are registered in the container.

Unfortunately this method is internal 😒 and it didn't seem like the default services would change. This resulted in replicating the registration in API versioning, but now there is a breaking change. It seems I'll have to go back to resorting to Reflection in order to call the internal method to prevent this from happening again. 😣

The fix isn't terribly difficult. I should be able to get it out by tomorrow. In the meantime, you can either wait or go back to a version < 7.2.0. The sample project targets 7.0.1 and it definitely works.

Извините за это. Спасибо за ваше терпение

@ghost
Copy link
Author

ghost commented Aug 7, 2019

With Microsoft.AspNet.OData 7.1.0 it works fine. Thank you!
We'll be waiting fix in new versions

@ghost ghost closed this as completed Aug 7, 2019
@commonsensesoftware
Copy link
Collaborator

No problem.

I also realized that the issue is just missing service registrations. This means that you should be able to make 7.2.0 work if you really want to by adding the following container builder configurations to the callback registered in MapVersionedODataRoutes:

builder.AddService<SkipTokenQueryValidator>(ServiceLifetime.Singleton);
builder.AddService<SkipTokenHandler, DefaultSkipTokenHandler>(ServiceLifetime.Singleton);

This missing registeration is the cause of the exception. Regardless, I'll be making the fix as this is a landmine waiting for others to step on. Thanks.

@commonsensesoftware
Copy link
Collaborator

FYI ... the patch for this has been published.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant