Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

OData Query Options

Chris Martinez edited this pageJan 1, 2023 ·6 revisions

query option conventions allow you to specify information for your OData services without having to rely solely on .NET attributes. There are a number of reasons why you might uses these conventions. The most common reasons are:

  • Centralized management and application of all OData query options
  • Define OData query options that cannot be expressed with any OData query attributes
  • Apply OData query options to services defined by controllers in external .NET assemblies

The parameter names generated are based on the name of the OData query option and the configuration of theODataUriResolver. OData supports query options without the system$ prefix. This is enabled or disabled by theODataUriResolver.EnableNoDollarQueryOptions property.

Attribute Model

The attribute model relies onModel Bound settings attributes and theEnableQueryAttribute. TheEnableQueryAttribute indicates API-specific options that might be too restrictive or unapplicable to specific models. Consider the following model and controller definitions.

usingSystem;usingMicrosoft.AspNet.OData.Query;usingstaticMicrosoft.AspNet.OData.Query.SelectExpandType;[Select][Select("effectiveDate",SelectType=Disabled)]publicclassOrder{publicintId{get;set;}publicDateTimeCreatedDate{get;set;}=DateTime.Now;publicDateTimeEffectiveDate{get;set;}=DateTime.Now;publicstringCustomer{get;set;}publicstringDescription{get;set;}}

ASP.NET Web API with OData

usingAsp.Versioning;usingAsp.Versioning.OData;usingMicrosoft.AspNet.OData;usingMicrosoft.AspNet.OData.Routing;usingMicrosoft.Web.Http;usingSystem.Web.Http;usingSystem.Web.Http.Description;usingstaticMicrosoft.AspNet.OData.Query.AllowedQueryOptions;usingstaticSystem.Net.HttpStatusCode;usingstaticSystem.DateTime;[ApiVersion(1.0)][ODataRoutePrefix("Orders")]publicclassOrdersController:ODataController{[ODataRoute][Produces("application/json")][ProducesResponseType(typeof(ODataValue<IEnumerable<Order>>),Status200OK)][EnableQuery(MaxTop=100,AllowedQueryOptions=Select|Top|Skip|Count)]publicIQueryable<Order>Get(){varorders=new[]{newOrder(){Id=1,Customer="John Doe"},newOrder(){Id=2,Customer="John Doe"},newOrder(){Id=3,Customer="Jane Doe",EffectiveDate=UtcNow.AddDays(7d)}};returnorders.AsQueryable();}[ODataRoute("{key}")][Produces("application/json")][ProducesResponseType(typeof(Order),Status200OK)][ProducesResponseType(Status404NotFound)][EnableQuery(AllowedQueryOptions=Select)]publicSingleResult<Order>Get(intkey){varorders=new[]{newOrder(){Id=key,Customer="John Doe"}};returnSingleResult.Create(orders.AsQueryable());}}

ASP.NET Core with OData

usingAsp.Versioning;usingAsp.Versioning.OData;usingMicrosoft.AspNetCore.Mvc;usingMicrosoft.AspNetCore.OData.Query;usingMicrosoft.AspNetCore.OData.Results;usingMicrosoft.AspNetCore.OData.Routing.Controllers;usingstaticMicrosoft.AspNetCore.Http.StatusCodes;usingstaticMicrosoft.AspNetCore.OData.Query.AllowedQueryOptions;usingstaticSystem.DateTime;[ApiVersion(1.0)]publicclassOrdersController:ODataController{[Produces("application/json")][ProducesResponseType(typeof(ODataValue<IEnumerable<Order>>),Status200OK)][EnableQuery(MaxTop=100,AllowedQueryOptions=Select|Top|Skip|Count)]publicIQueryable<Order>Get(){varorders=new[]{newOrder(){Id=1,Customer="John Doe"},newOrder(){Id=2,Customer="John Doe"},newOrder(){Id=3,Customer="Jane Doe",EffectiveDate=UtcNow.AddDays(7d)}};returnorders.AsQueryable();}[Produces("application/json")][ProducesResponseType(typeof(Order),Status200OK)][ProducesResponseType(Status404NotFound)][EnableQuery(AllowedQueryOptions=Select)]publicSingleResult<Order>Get(intkey){varorders=new[]{newOrder(){Id=key,Customer="John Doe"}};returnSingleResult.Create(orders.AsQueryable());}}

The OData API Explorer will discover and add add the following parameters for an entity set query:

NameDescriptionParameter TypeData Type
$selectLimits the properties returned in the result. The allowed properties are: id, createdDate, customer, description.querystring
$topLimits the number of items returned from a collection. The maximum value is 100.queryinteger
$skipExcludes the specified number of items of the queried collection from the result.queryinteger

Convention Model

The convention model relies onModel Bound settings via the fluent API of theODataModelBuilderand theEnableQueryAttribute. TheEnableQueryAttribute indicates API-specific options that might be too restrictive or unapplicable to specific models. Consider the following model and controller definitions.

publicclassPerson{publicintId{get;set;}publicstringFirstName{get;set;}publicstringLastName{get;set;}publicstringEmail{get;set;}publicstringPhone{get;set;}}publicclassPersonModelConfiguration:IModelConfiguration{publicvoidApply(ODataModelBuilderbuilder,ApiVersionapiVersion,stringroutePrefix){varperson=builder.EntitySet<Person>("People").EntityType;person.HasKey( p=>p.Id);// configure model bound conventionsperson.Select().OrderBy("firstName","lastName");if(apiVersion<ApiVersions.V3){person.Ignore( p=>p.Phone);}if(apiVersion<=ApiVersions.V1){person.Ignore( p=>p.Email);}if(apiVersion>ApiVersions.V1){varfunction=person.Collection.Function("NewHires");function.Parameter<DateTime>("Since");function.ReturnsFromEntitySet<Person>("People");}if(apiVersion>ApiVersions.V2){person.Action("Promote").Parameter<string>("title");}}}

ASP.NET Web API with OData

usingAsp.Versioning;usingAsp.Versioning.OData;usingMicrosoft.AspNet.OData;usingMicrosoft.AspNet.OData.Routing;usingMicrosoft.Web.Http;usingSystem.Web.Http;usingSystem.Web.Http.Description;usingstaticMicrosoft.AspNet.OData.Query.AllowedQueryOptions;usingstaticSystem.Net.HttpStatusCode;usingstaticSystem.DateTime;publicclassPeopleController:ODataController{[HttpGet][ResponseType(typeof(ODataValue<IEnumerable<Person>>))]publicIHttpActionResultGet(ODataQueryOptions<Person>options){varvalidationSettings=newODataValidationSettings(){AllowedQueryOptions=Select|OrderBy|Top|Skip|Count,AllowedOrderByProperties={"firstName","lastName"},AllowedArithmeticOperators=AllowedArithmeticOperators.None,AllowedFunctions=AllowedFunctions.None,AllowedLogicalOperators=AllowedLogicalOperators.None,MaxOrderByNodeCount=2,MaxTop=100,};try{options.Validate(validationSettings);}catch(ODataException){returnBadRequest();}varpeople=new[]{newPerson(){Id=1,FirstName="John",LastName="Doe",Email="john.doe@somewhere.com",Phone="555-987-1234",},newPerson(){Id=2,FirstName="Bob",LastName="Smith",Email="bob.smith@somewhere.com",Phone="555-654-4321",},newPerson(){Id=3,FirstName="Jane",LastName="Doe",Email="jane.doe@somewhere.com",Phone="555-789-3456",}};returnthis.Success(options.ApplyTo(people.AsQueryable()));}[HttpGet][ResponseType(typeof(Person))]publicIHttpActionResultGet(intkey,ODataQueryOptions<Person>options){varpeople=new[]{newPerson(){Id=key,FirstName="John",LastName="Doe",Email="john.doe@somewhere.com",Phone="555-987-1234",}};varquery=options.ApplyTo(people.AsQueryable();returnthis.SuccessOrNotFound(query).SingleOrDefault());}}

ASP.NET Core with OData

usingAsp.Versioning;usingAsp.Versioning.OData;usingMicrosoft.AspNetCore.Mvc;usingMicrosoft.AspNetCore.OData.Query;usingMicrosoft.AspNetCore.OData.Results;usingMicrosoft.AspNetCore.OData.Routing.Controllers;usingstaticMicrosoft.AspNetCore.Http.StatusCodes;usingstaticMicrosoft.AspNetCore.OData.Query.AllowedQueryOptions;usingstaticSystem.DateTime;publicclassPeopleController:ODataController{[Produces("application/json")][ProducesResponseType(typeof(ODataValue<IEnumerable<Person>>),Status200OK)]publicIActionResultGet(ODataQueryOptions<Person>options){varvalidationSettings=newODataValidationSettings(){AllowedQueryOptions=Select|OrderBy|Top|Skip|Count,AllowedOrderByProperties={"firstName","lastName"},AllowedArithmeticOperators=AllowedArithmeticOperators.None,AllowedFunctions=AllowedFunctions.None,AllowedLogicalOperators=AllowedLogicalOperators.None,MaxOrderByNodeCount=2,MaxTop=100,};try{options.Validate(validationSettings);}catch(ODataException){returnBadRequest();}varpeople=new[]{newPerson(){Id=1,FirstName="John",LastName="Doe",Email="john.doe@somewhere.com",Phone="555-987-1234",},newPerson(){Id=2,FirstName="Bob",LastName="Smith",Email="bob.smith@somewhere.com",Phone="555-654-4321",},newPerson(){Id=3,FirstName="Jane",LastName="Doe",Email="jane.doe@somewhere.com",Phone="555-789-3456",}};returnOk(options.ApplyTo(people.AsQueryable()));}[Produces("application/json")][ProducesResponseType(typeof(Person),Status200OK)][ProducesResponseType(Status404NotFound)]publicIActionResultGet(intkey,ODataQueryOptions<Person>options){varpeople=new[]{newPerson(){Id=key,FirstName="John",LastName="Doe",Email="john.doe@somewhere.com",Phone="555-987-1234",}};varperson=options.ApplyTo(people.AsQueryable()).SingleOrDefault();if(person==null){returnNotFound();}returnOk(person);}}

Conventions

If you only define OData query options imperatively usingODataQuerySettings andODataValidationSettings, then there are no attributes or Entity Data Model (EDM) data annotations to explore the query options from. In this scenario, you can use the conventions in the API Explorer extensions to document any query option setting that can be defined byODataQuerySettings orODataValidationSettings.

.AddODataApiExplorer( options=>{varqueryOptions=options.QueryOptions;queryOptions.Controller<V2.PeopleController>().Action( c=>c.Get(default(ODataQueryOptions<Person>))).Allow(Skip|Count).AllowTop(100);queryOptions.Controller<V3.PeopleController>().Action( c=>c.Get(default(ODataQueryOptions<Person>))).Allow(Skip|Count).AllowTop(100);});

The OData API Explorer will discover and add add the following parameters for an entity set query:

NameDescriptionParameter TypeData Type
$selectLimits the properties returned in the result.querystring
$orderbySpecifies the order in which results are returned. The allowed properties are: firstName, lastName.querystring
$topLimits the number of items returned from a collection. The maximum value is 100.queryinteger
$skipExcludes the specified number of items of the queried collection from the result.queryinteger

Parameter Descriptions

While each OData query option has a default provided description, the description can be changed by providing a custom description. Descriptions are generated by theIODataQueryOptionDescriptionProvider:

publicinterfaceIODataQueryOptionDescriptionProvider{stringDescribe(AllowedQueryOptionsqueryOption,ODataQueryOptionDescriptionContextcontext);}

Note: AlthoughAllowedQueryOptions is a bitwise enumeration, only a single query option value is ever passed

You can change the default description by implementing your ownIODataQueryOptionDescriptionProvider or extending the built-inDefaultODataQueryOptionDescriptionProvider. The implementation is updated in the OData API Explorer options using:

AddODataApiExplorer( options=>options.QueryOptions.DescriptionProvider=newMyQueryOptionDescriptor());

Custom Conventions

You can also define custom conventions via theIODataQueryOptionsConvention interface and add them to the builder:

publicinterfaceIODataQueryOptionsConvention{voidApplyTo(ApiDescriptionapiDescription);}
AddODataApiExplorer( options=>options.QueryOptions.Add(newMyODataQueryOptionsConvention()));

Query Options with Partial OData

OData supports query capabilities without using the full OData stack. Consider the following controller, which is not an OData controller, but uses OData query options:

[ApiVersion(1.0)][ApiController][Route("[controller]")]publicclassBooksController:ControllerBase{[HttpGet][Produces("application/json")][ProducesResponseType(typeof(IEnumerable<Book>),200)]publicIActionResultGet(ODataQueryOptions<Book>options)=>Ok(options.ApplyTo(books.AsQueryable()));}

When OData query capabilities are used this way, query options can be discovered viaEnableQueryAttribute or via the API Explorer extensions. Unfortunately, these are both ultimately limited to what can be expressed viaODataQuerySettings andODataValidationSettings, which does not cover the gambit of all possible OData query options; for example, the allowable$filter properties. These other properties can be configured viaModel Bound settings, but without using the full OData stack there is no Entity Data Model (EDM) to retrieve these annotations from.

To address this limitation, OData query options can now also be explored using an ad hoc EDM. This EDM only exists for the purposes of query option exploration. Using an ad hoc EDM does not opt into other OData feature and only exists during exploration. ApplyingModel Bound settings to an ad hoc model is almost identical to the normal method. If you want to use attributes, just apply them to your model.

[Filter("author","published")]publicclassBook{publicstringId{get;set;}publicstringAuthor{get;set;}publicstringTitle{get;set;}publicintPublished{get;set;}}

Every action that appears to beOData-like will automatically be discovered and its model explored. Discovered models are registered as a complex type by default. If you prefer to use entities or need additional control over the applied settings, you can use conventions as well.

AddODataApiExplorer(    options=>{options.AdHocModelBuilder.DefaultModelConfiguration=(builder,version,prefix)=>{builder.ComplexType<Book>().Filter("author","published");};})

Note that theAdHocModelBuilder is part of theODataApiExplorerOptions as opposed toODataApiVersioningOptions. If you have numerous models and would like to break the settings into different configurations, you can still useIModelConfiguration.IModelConfiguration instances are automatically discovered and injected the same way as they are when using the full OData stack.

publicclassBookConfiguration:IModelConfiguration{publicvoidApply(ODataModelBuilderbuilder,ApiVersionapiVersion,string?routePrefix){builder.EntitySet<Book>("Books").EntityType.Filter("author","published");}}

Model configuration for an ad hoc model; theroutePrefix will always benull.

There is no distinction between anIModelConfiguration that is used for ad hoc EDM exploration versus normal model registration. It is unlikely that you would be mixing the full and partial OData stack. If you are mixing use cases, then you can tell the difference between models from the provided API version. There should be no scenario where a model is registered two different ways for the same API version.

Clone this wiki locally

[8]ページ先頭

©2009-2025 Movatter.jp