web-dev-qa-db-ja.com

OData V4はサーバー側で$ filterを変更します

コントローラー内のフィルターを変更し、変更されたフィルターに基づいてデータを返すことができるようにしたいと思います。

したがって、サーバー側にODataQueryOptionsパラメーターがあり、これを使用してFilterQueryOptionを確認できます。

フィルターが「$ filter = ID eq -1」のようなものであるとしましょう。サーバー側でIDに「-1」が表示された場合、これはユーザーがすべてのレコードを選択したいことを示しています。

「$ filter = ID eq -1」を「$ filter = ID ne -1」に変更しようとしましたが、これはFilter.RawValueを設定することですべてを提供しますが、これは読み取り専用です。
新しいFilterQueryOptionを作成しようとしましたが、これにはODataQueryContextとODataQueryOptionParserが必要ですが、作成方法がわかりません。

次に、Filter = Nullを設定してから、コントローラにブレークポイントを設定してこれをイミディエイトウィンドウで確認すると機能するように見えるApplyToを使用しましたが、コントローラのGETメソッドを終了すると、元に戻ります。 URLで渡されたものに。

この記事では、非常によく似た " WebAPI OData QueryOptions.Filterを変更する最善の方法 "を実行する方法について説明しますが、コントローラーのGETメソッドを終了すると、URLクエリフィルターに戻ります。

サンプルコードで更新

[EnableQuery]
[HttpGet]
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions)
{
    if (queryOptions.Filter != null)
    {
        var url = queryOptions.Request.RequestUri.AbsoluteUri;
        string filter = queryOptions.Filter.RawValue;

        url = url.Replace("$filter=ID%20eq%201", "$filter=ID%20eq%202");
        var req = new HttpRequestMessage(HttpMethod.Get, url);

        queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req);
    }

    IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
    return query as IQueryable<Product>;
}

このコードを実行しても製品は返されません。これは、URLの元のクエリが製品1を必要としていて、製品1のIDフィルターを製品2と交換したためです。
SQLプロファイラーを実行すると、「Select * from Product WHERE ID = 1 AND ID = 2」のようなものが追加されたことがわかります。

[〜#〜] but [〜#〜]$ topを置き換えて同じことを試しても、うまく機能します。

[EnableQuery]
[HttpGet]
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions)
{
    if (queryOptions.Top != null)
    {
        var url = queryOptions.Request.RequestUri.AbsoluteUri;
        string filter = queryOptions.Top.RawValue;

        url = url.Replace("$top=2", "$top=1");
        var req = new HttpRequestMessage(HttpMethod.Get, url);

        queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req);
    }

    IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
    return query as IQueryable<Product>;
}

END RESULT
マイクロソフトの協力を得て。これは、フィルター、カウント、およびページングをサポートする最終出力です。

using System.Net.Http;
using System.Web.OData;
using System.Web.OData.Extensions;
using System.Web.OData.Query;

/// <summary>
/// Used to create custom filters, selects, groupings, ordering, etc...
/// </summary>
public class CustomEnableQueryAttribute : EnableQueryAttribute
{
    public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
    {
        IQueryable result = default(IQueryable);

        // get the original request before the alterations
        HttpRequestMessage originalRequest = queryOptions.Request;

        // get the original URL before the alterations
        string url = originalRequest.RequestUri.AbsoluteUri;

        // rebuild the URL if it contains a specific filter for "ID = 0" to select all records
        if (queryOptions.Filter != null && url.Contains("$filter=ID%20eq%200")) 
        {
            // apply the new filter
            url = url.Replace("$filter=ID%20eq%200", "$filter=ID%20ne%200");

            // build a new request for the filter
            HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url);

            // reset the query options with the new request
            queryOptions = new ODataQueryOptions(queryOptions.Context, req);
        }

        // set a top filter if one was not supplied
        if (queryOptions.Top == null) 
        {
            // apply the query options with the new top filter
            result = queryOptions.ApplyTo(queryable, new ODataQuerySettings { PageSize = 100 });
        } 
        else 
        {
            // apply any pending information that was not previously applied
            result = queryOptions.ApplyTo(queryable);
        }

        // add the NextLink if one exists
        if (queryOptions.Request.ODataProperties().NextLink != null) 
        {
            originalRequest.ODataProperties().NextLink = queryOptions.Request.ODataProperties().NextLink;
        }
        // add the TotalCount if one exists
        if (queryOptions.Request.ODataProperties().TotalCount != null) 
        {
            originalRequest.ODataProperties().TotalCount = queryOptions.Request.ODataProperties().TotalCount;
        }

        // return all results
        return result;
    }
}
16
goroth

[EnableQuery]属性を削除すると、シナリオが機能します。この属性を使用した後、OData/WebApiはコントローラーにデータを返した後に元のクエリオプションを適用します。コントローラーメソッドで既に適用している場合は、その属性を使用しないでください。 。

ただし、クエリオプションに$ selectが含まれている場合、結果のタイプがProductではないため、これらのコードは機能しません。ラッパーを使用して$ selectの結果を表すため、次のように使用することをお勧めします。

カスタマイズされたEnableQueryAttributeを作成します

public class MyEnableQueryAttribute : EnableQueryAttribute
{
    public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
    {
        if (queryOptions.Filter != null)
        {
            queryOptions.ApplyTo(queryable);
            var url = queryOptions.Request.RequestUri.AbsoluteUri;

            url = url.Replace("$filter=Id%20eq%201", "$filter=Id%20eq%202");
            var req = new HttpRequestMessage(HttpMethod.Get, url);

            queryOptions = new ODataQueryOptions(queryOptions.Context, req);
        }

        return queryOptions.ApplyTo(queryable);
    }
}

コントローラーのメソッドでこの属性を使用

[MyEnableQueryAttribute]
public IHttpActionResult Get()
{
    return Ok(_products);
}

これがあなたの問題を解決することを願っています、ありがとう!

ファン。

10
Fan Ouyang

@Chris Schallerへの返信として、私は以下のように自分のソリューションを投稿します。

public class CustomEnableQueryAttribute : EnableQueryAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var url = actionContext.Request.RequestUri.OriginalString;

        //change something in original url, 
        //for example change all A charaters to B charaters,
        //consider decoding url using WebUtility.UrlDecode() if necessary
        var newUrl = ModifyUrl(url); 

        actionContext.Request.RequestUri = new Uri(newUrl);
        base.OnActionExecuting(actionContext);
    }
}
5
Afshar Mohebbi