ASP.NET Web API 2 ' de OData sorgu seçeneklerini desteklemeSupporting OData Query Options in ASP.NET Web API 2

, Mike te sonby Mike Wasson

Kod örnekleri ile bu genel bakış, ASP.NET 4. x için ASP.NET Web API 2 ' deki desteklenen OData sorgu seçeneklerini gösterir.This overview with code examples demonstrates the supporting OData Query Options in ASP.NET Web API 2 for ASP.NET 4.x.

OData, bir OData sorgusunu değiştirmek için kullanılabilecek parametreleri tanımlar.OData defines parameters that can be used to modify an OData query. İstemci bu parametreleri istek URI 'sinin sorgu dizesinde gönderir.The client sends these parameters in the query string of the request URI. Örneğin, sonuçları sıralamak için bir istemci $orderby parametresini kullanır:For example, to sort the results, a client uses the $orderby parameter:

http://localhost/Products?$orderby=Name

OData belirtimi Bu parametre sorgu seçenekleriniçağırır.The OData specification calls these parameters query options. Projenizdeki herhangi bir Web API denetleyicisi için OData sorgu seçeneklerini etkinleştirebilir — denetleyicinin bir OData uç noktası olması gerekmez.You can enable OData query options for any Web API controller in your project — the controller does not need to be an OData endpoint. Bu, herhangi bir Web API uygulamasına filtreleme ve sıralama gibi özellikleri eklemenin kolay bir yolunu sunar.This gives you a convenient way to add features such as filtering and sorting to any Web API application.

Sorgu seçeneklerini etkinleştirmeden önce lütfen OData Güvenlik Kılavuzukonusunu okuyun.Before enabling query options, please read the topic OData Security Guidance.

OData sorgu seçeneklerini etkinleştirmeEnabling OData Query Options

Web API 'SI aşağıdaki OData sorgu seçeneklerini destekler:Web API supports the following OData query options:

SeçenekOption AçıklamaDescription
$expand$expand İlgili varlıkları satır içi genişletir.Expands related entities inline.
$filter$filter Sonuçları Boole koşuluna göre filtreler.Filters the results, based on a Boolean condition.
$inlinecount$inlinecount Sunucuya, yanıttaki eşleşen varlıkların toplam sayısını içermesini söyler.Tells the server to include the total count of matching entities in the response. (Sunucu tarafı sayfalama için kullanışlıdır.)(Useful for server-side paging.)
$orderby$orderby Sonuçları sıralar.Sorts the results.
$select$select Yanıta hangi özelliklerin ekleneceğini seçer.Selects which properties to include in the response.
$skip$skip İlk n sonucu atlar.Skips the first n results.
$top$top Sonuçları yalnızca ilk n sonucunu döndürür.Returns only the first n the results.

OData sorgu seçeneklerini kullanmak için bunları açıkça etkinleştirmeniz gerekir.To use OData query options, you must enable them explicitly. Uygulamayı tüm uygulama için küresel olarak etkinleştirebilir veya belirli denetleyiciler veya belirli eylemler için etkinleştirebilirsiniz.You can enable them globally for the entire application, or enable them for specific controllers or specific actions.

OData sorgu seçeneklerini küresel olarak etkinleştirmek için, başlangıç sırasında HttpConfiguration sınıfında EnableQuerySupport ' u çağırın:To enable OData query options globally, call EnableQuerySupport on the HttpConfiguration class at startup:

public static void Register(HttpConfiguration config)
{
    // ...

    config.EnableQuerySupport();

    // ...
}

EnableQuerySupport yöntemi bir IQueryable türü döndüren herhangi bir denetleyici eylemi için genel olarak sorgu seçeneklerini sunar.The EnableQuerySupport method enables query options globally for any controller action that returns an IQueryable type. Sorgu seçeneklerinin tüm uygulama için etkinleştirilmesini istemiyorsanız, eylem yöntemine [Queryable] özniteliğini ekleyerek belirli denetleyici eylemleri için bunları etkinleştirebilirsiniz.If you don't want query options enabled for the entire application, you can enable them for specific controller actions by adding the [Queryable] attribute to the action method.

public class ProductsController : ApiController
{
    [Queryable]
    IQueryable<Product> Get() {}
}

Örnek SorgularExample Queries

Bu bölümde, OData sorgu seçenekleri kullanılarak mümkün olan sorgu türleri gösterilmektedir.This section shows the types of queries that are possible using the OData query options. Sorgu seçenekleri hakkında ayrıntılı bilgi için www.OData.orgadresindeki OData belgelerine bakın.For specific details about the query options, refer to the OData documentation at www.odata.org.

$Expand ve $select hakkında daha fazla bilgi için bkz. ASP.NET Web API OData içinde $SELECT, $expand ve $value kullanma.For information about $expand and $select, see Using $select, $expand, and $value in ASP.NET Web API OData.

İstemci odaklı disk belleğiClient-Driven Paging

Büyük varlık kümelerinde istemci, sonuçların sayısını sınırlamak isteyebilir.For large entity sets, the client might want to limit the number of results. Örneğin, bir istemci bir sonraki sonuç sayfasını almak için "ileri" bağlantıları ile her seferinde 10 giriş gösterebilir.For example, a client might show 10 entries at a time, with "next" links to get the next page of results. Bunu yapmak için, istemci $top ve $skip seçeneklerini kullanır.To do this, the client uses the $top and $skip options.

http://localhost/Products?$top=10&$skip=20

$Top seçeneği döndürülecek en fazla girdi sayısını verir ve $skip seçeneği, atlanacak girdi sayısını verir.The $top option gives the maximum number of entries to return, and the $skip option gives the number of entries to skip. Önceki örnek, girdileri 21 ile 30 arasında getirir.The previous example fetches entries 21 through 30.

FiltrelemeFiltering

$Filter seçeneği, bir istemcinin Boole ifadesi uygulayarak sonuçları filtrelemesine olanak sağlar.The $filter option lets a client filter the results by applying a Boolean expression. Filtre ifadeleri oldukça güçlüdür; mantıksal ve aritmetik işleçler, dize işlevleri ve Tarih işlevleri içerirler.The filter expressions are quite powerful; they include logical and arithmetic operators, string functions, and date functions.

Kategori "Toys" değerine eşit olan tüm ürünleri döndürür.Return all products with category equal to "Toys". http://localhost/Products?$filter=Category EQ ' Toys 'http://localhost/Products?$filter=Category eq 'Toys'
Fiyatı 10 ' dan küçük olan tüm ürünleri döndürün.Return all products with price less than 10. http://localhost/Products?$filter=Price lt 10http://localhost/Products?$filter=Price lt 10
Mantıksal işleçler: Price >= 5 ve Price <= 15 olan tüm ürünleri döndürün.Logical operators: Return all products where price >= 5 and price <= 15. http://localhost/Products?$filter=Price GE 5 ve fiyat Le 15http://localhost/Products?$filter=Price ge 5 and Price le 15
Dize işlevleri: adında "ZZ" olan tüm ürünleri döndürün.String functions: Return all products with "zz" in the name. http://localhost/Products?$filter=substringof('zz',Name)
Tarih işlevleri: 2005 sonrasında ReleaseDate ile tüm ürünleri döndürün.Date functions: Return all products with ReleaseDate after 2005. http://localhost/Products?$filter=year(ReleaseDate) gt 2005http://localhost/Products?$filter=year(ReleaseDate) gt 2005

SıralamaSorting

Sonuçları sıralamak için $orderby filtresini kullanın.To sort the results, use the $orderby filter.

Fiyata göre sıralayın.Sort by price. http://localhost/Products?$orderby=Price
Fiyata göre azalan sırada sıralayın (en yüksek-en düşük).Sort by price in descending order (highest to lowest). http://localhost/Products?$orderby=Price desc
Kategoriye göre sıralayın ve ardından kategoriler içinde azalan sırada fiyata göre sıralayın.Sort by category, then sort by price in descending order within categories. http://localhost/odata/Products?$orderby=Category,Price desc

Sunucu odaklı sayfalamaServer-Driven Paging

Veritabanınız milyonlarca kayıt içeriyorsa, bunların tümünü tek bir yükte göndermek istemezsiniz.If your database contains millions of records, you don't want to send them all in one payload. Bunu engellemek için sunucu tek bir yanıtta gönderdiği giriş sayısını sınırlayabilir.To prevent this, the server can limit the number of entries that it sends in a single response. Sunucu Sayfalamayı etkinleştirmek için, sorgulanabilir özniteliğinde PageSize özelliğini ayarlayın.To enable server paging, set the PageSize property in the Queryable attribute. Değer döndürülecek en fazla girdi sayısıdır.The value is the maximum number of entries to return.

[Queryable(PageSize=10)]
public IQueryable<Product> Get() 
{
    return products.AsQueryable();
}

Denetleyiciniz OData biçimi döndürürse, yanıt gövdesi sonraki veri sayfasına bir bağlantı içerir:If your controller returns OData format, the response body will contain a link to the next page of data:

{
  "odata.metadata":"http://localhost/$metadata#Products",
  "value":[
    { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
    { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
    // Others not shown
  ],
  "odata.nextLink":"http://localhost/Products?$skip=10"
}

İstemci bu bağlantıyı bir sonraki sayfayı getirmek için kullanabilir.The client can use this link to fetch the next page. Sonuç kümesindeki toplam giriş sayısını öğrenmek için, istemci $inlinecount sorgu seçeneğini "AllPages" değeriyle ayarlayabilir.To learn the total number of entries in the result set, the client can set the $inlinecount query option with the value "allpages".

http://localhost/Products?$inlinecount=allpages

"AllPages" değeri, sunucuya yanıttaki toplam sayıyı dahil etmek için şunu söyler:The value "allpages" tells the server to include the total count in the response:

{
  "odata.metadata":"http://localhost/$metadata#Products",
  "odata.count":"50",
  "value":[
    { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
    { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
    // Others not shown
  ]
}

Note

Sonraki sayfa bağlantıları ve satır içi sayısı her ikisi de OData biçimi gerektirir.Next-page links and inline count both require OData format. Bunun nedeni, OData 'in bağlantıyı ve saymayı tutmak için yanıt gövdesinde özel alanlar tanımlamasının nedenidir.The reason is that OData defines special fields in the response body to hold the link and count.

OData olmayan biçimler için, sorgu sonuçlarını bir **PageResult < > ** nesnesine sarmalayarak, sonraki sayfa bağlantılarını ve satır içi sayıyı desteklemek yine de mümkündür.For non-OData formats, it is still possible to support next-page links and inline count, by wrapping the query results in a PageResult<T> object. Ancak, biraz daha fazla kod gerektirir.However, it requires a bit more code. Aşağıda bir örnek verilmiştir:Here is an example:

public PageResult<Product> Get(ODataQueryOptions<Product> options)
{
    ODataQuerySettings settings = new ODataQuerySettings()
    {
        PageSize = 5
    };

    IQueryable results = options.ApplyTo(_products.AsQueryable(), settings);

    return new PageResult<Product>(
        results as IEnumerable<Product>, 
        Request.GetNextPageLink(), 
        Request.GetInlineCount());
}

Örnek bir JSON yanıtı aşağıda verilmiştir:Here is an example JSON response:

{
  "Items": [
    { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
    { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },

    // Others not shown
    
  ],
  "NextPageLink": "http://localhost/api/values?$inlinecount=allpages&$skip=10",
  "Count": 50
}

Sorgu seçeneklerini sınırlamaLimiting the Query Options

Sorgu seçenekleri istemciye sunucusunda çalıştırılan sorgu üzerinde çok fazla denetim sağlar.The query options give the client a lot of control over the query that is run on the server. Bazı durumlarda, güvenlik veya performans nedenleriyle kullanılabilir seçenekleri sınırlamak isteyebilirsiniz.In some cases, you might want to limit the available options for security or performance reasons. [Queryable] özniteliğinde bunun için bazı yerleşik özellikler vardır.The [Queryable] attribute has some built in properties for this. Aşağıda bazı örnekler verilmiştir.Here are some examples.

Yalnızca $skip ve $top, sayfalamayı desteklemeye ve başka hiçbir şey yapmasına izin ver:Allow only $skip and $top, to support paging and nothing else:

[Queryable(AllowedQueryOptions=
    AllowedQueryOptions.Skip | AllowedQueryOptions.Top)]

Veritabanında dizinlenmemiş özelliklerde sıralamayı engellemek için yalnızca belirli özelliklere göre sıralamaya izin ver:Allow ordering only by certain properties, to prevent sorting on properties that are not indexed in the database:

[Queryable(AllowedOrderByProperties="Id")] // comma-separated list of properties

"EQ" mantıksal işlevine izin verin, ancak başka mantıksal işlevler yoktur:Allow the "eq" logical function but no other logical functions:

[Queryable(AllowedLogicalOperators=AllowedLogicalOperators.Equal)]

Aritmetik işleçlere izin vermeyin:Do not allow any arithmetic operators:

[Queryable(AllowedArithmeticOperators=AllowedArithmeticOperators.None)]

Bir QueryableAttribute örneği oluşturarak ve bunu EnableQuerySupport işlevine geçirerek, seçenekleri küresel olarak kısıtlayabilirsiniz:You can restrict options globally by constructing a QueryableAttribute instance and passing it to the EnableQuerySupport function:

var queryAttribute = new QueryableAttribute()
{
    AllowedQueryOptions = AllowedQueryOptions.Top | AllowedQueryOptions.Skip,
    MaxTop = 100
};
                
config.EnableQuerySupport(queryAttribute);

Sorgu seçenekleri doğrudan çağrılıyorInvoking Query Options Directly

[Sorgulanabilir] özniteliğini kullanmak yerine, doğrudan denetleyicinizde sorgu seçeneklerini çağırabilirsiniz.Instead of using the [Queryable] attribute, you can invoke the query options directly in your controller. Bunu yapmak için, Controller yöntemine bir ODataQueryOptions parametresi ekleyin.To do so, add an ODataQueryOptions parameter to the controller method. Bu durumda, [Queryable] özniteliğine ihtiyacınız yoktur.In this case, you don't need the [Queryable] attribute.

public IQueryable<Product> Get(ODataQueryOptions opts)
{
    var settings = new ODataValidationSettings()
    {
        // Initialize settings as needed.
        AllowedFunctions = AllowedFunctions.AllMathFunctions
    };

    opts.Validate(settings);

    IQueryable results = opts.ApplyTo(products.AsQueryable());
    return results as IQueryable<Product>;
}

Web API 'si, ODataQueryOptions 'ı URI sorgu dizesinden doldurur.Web API populates the ODataQueryOptions from the URI query string. Sorguyu uygulamak için ApplyTo yöntemine bir IQueryable geçirin.To apply the query, pass an IQueryable to the ApplyTo method. Yöntemi başka bir IQueryabledöndürür.The method returns another IQueryable.

Gelişmiş senaryolar için, bir IQueryable sorgu sağlayıcınız yoksa, ODataQueryOptions öğesini inceleyebilir ve sorgu seçeneklerini başka bir forma çevirebilirsiniz.For advanced scenarios, if you do not have an IQueryable query provider, you can examine the ODataQueryOptions and translate the query options into another form. (Örneğin, bkz. ıghurhar Naazaltı 'ın Web günlüğü gönderisi, OData sorgularını HQL 'ye çevirerekbir örnekde içerir.)(For example, see RaghuRam Nadiminti's blog post Translating OData queries to HQL, which also includes a sample.)

Sorgu doğrulamasıQuery Validation

[Queryable] özniteliği yürütmeden önce sorguyu doğrular.The [Queryable] attribute validates the query before executing it. Doğrulama adımı QueryableAttribute. ValidateQuery yönteminde gerçekleştirilir.The validation step is performed in the QueryableAttribute.ValidateQuery method. Doğrulama işlemini de özelleştirebilirsiniz.You can also customize the validation process.

Ayrıca bkz. OData Güvenlik Kılavuzu.Also see OData Security Guidance.

İlk olarak, Web. http. OData. Query. doğrulayıcılar ad alanında tanımlanan Doğrulayıcı sınıflarından birini geçersiz kılın.First, override one of the validator classes that is defined in the Web.Http.OData.Query.Validators namespace. Örneğin, aşağıdaki Doğrulayıcı sınıfı $orderby seçeneği için ' DESC ' seçeneğini devre dışı bırakır.For example, the following validator class disables the 'desc' option for the $orderby option.

public class MyOrderByValidator : OrderByQueryValidator
{
    // Disallow the 'desc' parameter for $orderby option.
    public override void Validate(OrderByQueryOption orderByOption,
                                    ODataValidationSettings validationSettings)
    {
        if (orderByOption.OrderByNodes.Any(
                node => node.Direction == OrderByDirection.Descending))
        {
            throw new ODataException("The 'desc' option is not supported.");
        }
        base.Validate(orderByOption, validationSettings);
    }
}

Validatequery yöntemini geçersiz kılmak Için [Queryable] özniteliğini alt sınıf yapın.Subclass the [Queryable] attribute to override the ValidateQuery method.

public class MyQueryableAttribute : QueryableAttribute
{
    public override void ValidateQuery(HttpRequestMessage request, 
        ODataQueryOptions queryOptions)
    {
        if (queryOptions.OrderBy != null)
        {
            queryOptions.OrderBy.Validator = new MyOrderByValidator();
        }
        base.ValidateQuery(request, queryOptions);
    }
}

Ardından özel öznitelerinizi genel olarak veya denetleyiciden göre ayarlayın:Then set your custom attribute either globally or per-controller:

// Globally:
config.EnableQuerySupport(new MyQueryableAttribute());

// Per controller:
public class ValuesController : ApiController
{
    [MyQueryable]
    public IQueryable<Product> Get()
    {
        return products.AsQueryable();
    }
}

ODataQueryOptions ' ı doğrudan kullanıyorsanız, seçenekler üzerinde doğrulayıcısı ayarlayın:If you are using ODataQueryOptions directly, set the validator on the options:

public IQueryable<Product> Get(ODataQueryOptions opts)
{
    if (opts.OrderBy != null)
    {
        opts.OrderBy.Validator = new MyOrderByValidator();
    }

    var settings = new ODataValidationSettings()
    {
        // Initialize settings as needed.
        AllowedFunctions = AllowedFunctions.AllMathFunctions
    };

    // Validate
    opts.Validate(settings);

    IQueryable results = opts.ApplyTo(products.AsQueryable());
    return results as IQueryable<Product>;
}