OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
854 stars 475 forks source link

Why ODataQueryOptions class is NOT working for ASPNet Core 3.1? #2266

Open htzhang2 opened 4 years ago

htzhang2 commented 4 years ago

I have a ASPNet Core 3.1 project, controller GET method is trying to use ODataQueryOptions as parameter:

using Microsoft.AspNet.OData.Query;

[HttpGet("api/products")] public async Task<ODataPaginationResult> GetProducts( [fromQuery] ODataQueryOptions opts) { // code to get products }

// However, on Swagger this opts parameter shows as body object. // On Postman, it show 415 (unsupported media type) if uri is http://localhost:[port]/api/products?$top=1

// Similar code works for ASPNet 4.7.2 project on Postman

Seems OData structure is different between ASPNet and Core. Please suggest a work-around on ASPNMet Core 3.1

UmairB commented 4 years ago

It is working for me on 3.1. Maybe include a sample of how you are configuring the odata pipeline and the EdmModel.

htzhang2 commented 4 years ago

//public void ConfigureServices(IServiceCollection services) {

services.AddOData(); services.AddMvcCore(options => { foreach (var outputFormatter in options.OutputFormatters.OfType().Where(x => x.SupportedMediaTypes.Count == 0)) { outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata")); }

            foreach (var inputFormatter in options.InputFormatters.OfType<InputFormatter>().Where(x => x.SupportedMediaTypes.Count == 0))
            {
                inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
            }
        })

// public void Configure(...) {

       app.UseMvc(routeBuilder =>
        {
            routeBuilder.EnableDependencyInjection();

            routeBuilder.Expand().Select().Filter().Count().OrderBy();
        });
htzhang2 commented 4 years ago

Try to create a EDMModel but don't know how to set routeName and routePrefix ?

  routeBuilder.MapODataServiceRoute("api", "api", GetEdmModel());

  private static IEdmModel GetEdmModel()
    {
        ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<EntityClaimsModel>("EntityClaims");
        return builder.GetEdmModel();
    }

// This code crashed my application

My controller:

[Route("api/claims/admin/ns/{nameSpace}/entities/{entityId}")] public class EntityClaimsController : ControllerBase {

    [HttpGet("odata/users")]
    [EnableQuery]
    public async Task<IActionResult> GetEntityUserClaimInfo(string nameSpace, string entityId, ODataQueryOptions opts)
htzhang2 commented 4 years ago

Log shows EntityClaimsModel has no key defined:

EventName: StatelessReplicaNewHealthReport Category: Health EventInstanceId { 92afcf39-5a54-41e2-b702-5b8abde7b568} PartitionId {c3502651-1f23-4277-94a3-7736f0138e29} ReplicaId 132431326198692503 SourceId=System.RA Property=ReplicaOpenStatus HealthState=Warning TTL=922337203685477 SequenceNumber=132431326394614196 Description='Replica had multiple failures during open on _Node_0. API call: IStatelessServiceInstance.Open(); Error = System.InvalidOperationException (-2146233079) The entity set 'EntityClaims' is based on type 'VWAC.Claims.ClaimsService.Domain.Model.EntityClaimsModel' that has no keys defined. at Microsoft.AspNet.OData.Builder.ODataModelBuilder.ValidateModel(IEdmModel model) at Microsoft.AspNet.OData.Builder.ODataConventionModelBuilder.ValidateModel(IEdmModel model) at Microsoft.AspNet.OData.Builder.ODataConventionModelBuilder.GetEdmModel()

But EntityClaimsModel does have key defined:

public class EntityClaimsModel : BaseModel { private readonly string entityId;

    /// <summary>Gets the entity identifier.</summary>
    /// <value>The entity identifier.</value>
    [Required]
    [Key]
    public string EntityId => entityId;

...

KenitoInc commented 4 years ago

Hey @htzhang2 It seems you using ODataQueryOptions in a Non-OData AspNetCore application. In the first example, you can try removing the [FromQuery] and see if it works.

In order to fully use OData, you can use ODataRoute instead of Route, ODataController instead of ControllerBase.

AmirSasson commented 3 years ago

hi @KenitoInc , removing the [FromQuery] works but not really an option as it breaks the external contract (serialized json rather than separated input parameters ). what might be a good way to work around this odd problem?