57

ASP .NET MVC 4 が提供するWebAPIを使用して作成された Web サービスがあります。 WebAPI が機能する最上位のレイヤーがOData クエリ( $filter$top、など$skip) を自動的に処理することはわかっていますが、フィルタリングを自分で処理したい場合はどうすればよいでしょうか? ?

データベースからデータを返すだけではありませんが、いくつかのプロパティを追加したり、いくつかの変換を行ったりする別のレイヤーがあります。そのため、すべてのデータをクエリし、それらを変換して、OData フィルタリングのために WebAPI クラスに返すのは良いことではありません。足りる。もちろん、それはひどく遅く、一般的にくだらないアイデアです。

OData クエリ パラメータを WebAPI エントリ ポイントから呼び出してデータを取得および変換する関数に伝達する方法はありますか?

たとえば/api/people?$skip=10&$top=10、サーバー上で GET を呼び出すと、次のようになります。

public IQueryable<Person> get() {
    return PersonService.get(SomethingAboutCurrentRequest.CurrentOData);
}

そしてでPersonService

public IQueryable<Person> getPeople(var ODataQueries) {
    IQueryable<ServerSidePerson> serverPeople = from p in dbContext.ServerSidePerson select p;
    // Make the OData queries
    // Skip
    serverPeople = serverPeople.Skip(ODataQueries.Skip);
    // Take
    serverPeople = serverPeople.Take(ODataQueries.Take);
    // And so on
    // ...

    // Then, convert them
    IQueryable<Person> people = Converter.convertPersonList(serverPeople);
    return people;
}
4

4 に答える 4

42

この古い投稿に出くわしたばかりで、OData クエリを自分で処理するのが非常に簡単になったため、この回答を追加しています。次に例を示します。

[HttpGet]
[ActionName("Example")]
public IEnumerable<Poco> GetExample(ODataQueryOptions<Poco> queryOptions)
{
    var data = new Poco[] { 
        new Poco() { id = 1, name = "one", type = "a" },
        new Poco() { id = 2, name = "two", type = "b" },
        new Poco() { id = 3, name = "three", type = "c" }
    };

    var t = new ODataValidationSettings() { MaxTop = 2 };
    queryOptions.Validate(t);

    //this is the method to filter using the OData framework
    //var s = new ODataQuerySettings() { PageSize = 1 };
    //var results = queryOptions.ApplyTo(data.AsQueryable(), s) as IEnumerable<Poco>;

    //or DIY
    var results = data;
    if (queryOptions.Skip != null) 
        results = results.Skip(queryOptions.Skip.Value);
    if (queryOptions.Top != null)
        results = results.Take(queryOptions.Top.Value);

    return results;
}

public class Poco
{
    public int id { get; set; }
    public string name { get; set; }
    public string type { get; set; }
}
于 2013-03-19T09:35:56.223 に答える
4

URLからのクエリはLINQ式ツリーに変換され、操作が返すIQueryableに対して実行されます。式を分析して、任意の方法で結果を提供できます。欠点は、IQueryableを実装する必要があることです。これは非常に簡単ではありません。興味がある場合は、このブログ投稿シリーズをご覧ください:http: //blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro.aspx。WCF Data Servicesについて説明していますが、WebAPIで使用されるフィルター式は非常に似ています。

于 2012-05-28T09:12:20.870 に答える
2

Web-api を使用する 1 つの方法は、顧客メッセージ ハンドラーhttp://www.asp.net/web-api/overview/working-with-http/http-message-handlersを使用することです。

以下のようなカスタム ハンドラを記述します。

public class CustomHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return base.SendAsync(request, cancellationToken).ContinueWith(
                (task) =>
                {
                    HttpResponseMessage response = task.Result;
                    var persons = response.Content.ReadAsAsync<IQueryable<Person>>().Result;
                    var persons2 = new List<Person>(); //This can be the modified model completely different
                    foreach (var item in persons)
                    {
                        item.Name = "changed"; // here you can change the data
                        //persons2.Add(....); //Depending on the results modify this custom model
                    }
                    //overwrite the response
                    response = new HttpResponseMessage<IEnumerable<Person>>(persons2); 
                    return response;
                }
            );
        }
    }

global.asax.cs に登録する

アプリケーション クラスのメソッド:

static void Configure(HttpConfiguration config)
 {
     config.MessageHandlers.Add(new CustomHandler()); 
 }

protected void Application_Start()
{
     ....
     .....
     //call the configure method
     Configure(GlobalConfiguration.Configuration);
 }
于 2012-05-28T10:07:31.080 に答える
0

私は WCF Data Services と asp.net mvc 3.5 でこのようなことをしましたが、少し面倒でした。

最初のステップは、skip オプションと top オプションが 2 回 (ユーザーが 1 回、実行時に 1 回) 適用されないようにパスを書き直すことです。

HttpModuleで書き換えました。BeginRequest メソッドには、次のようなコードがあります。

HttpApplication app = (HttpApplication)sender;
if (HttpContext.Current.Request.Path.Contains(YOUR_SVC))
{
    if (app.Request.Url.Query.Length > 0)
    {
        //skip questionmark
        string queryString = app.Request.Url.Query.Substring(1) 
                    .Replace("$filter=", "filter=")
                    .Replace("$orderby=", "orderby=")
                    .Replace("$top=", "top=")
                    .Replace("$skip=", "skip=");

                HttpContext.Current.RewritePath(app.Request.Path, "", queryString);
    }
}

次に、クエリ文字列を調べて、必要なパラメーターを取り出します。

if (HttpContext.Current.Request.QueryString["filter"] != null)
    var filter = HttpContext.Current.Request.QueryString["filter"] as string;

次に、フィルター文字列を分割し、それを sql ステートメントまたはその他の db コマンドに解析します。私の場合、NHibernateを使用していました。また、サポートするフィルター コマンドを制限することもできたので、作業が簡単になりました。たとえば、グループ化はしませんでした。ほとんどの場合、比較演算子のみです。

OData.orgにフィルター演算子のリストがあります。文字列を「and」と「or」で個々の句に分割します。各句をスペースで分割すると、[0] にプロパティ名、[1] に演算子、[2] に値を持つ 3 つの要素の配列が得られます。

句は Person に関するものになりますが、データベースから正しい結果を引き出すものにそれらを変換できると思います。

POSTSでは機能しないため、このアプローチを放棄しました。独自の Linq プロバイダーを作成する必要がありましたが、独自のフィルター文字列パーサーを作成することで理解しやすくなりました。

于 2012-05-28T12:24:53.557 に答える