2012-05-28 69 views
51

我必須使用ASP .NET MVC提供的的WebAPI 4. 我知道其中的WebAPI自動工作在頂部層處理的OData查詢Web服務取得(如$filter,$top,$skip),但如果我想自己處理過濾,該怎麼辦?ASP .NET MVC 4的WebAPI:手工處理的OData查詢

I don't simply return data from my database,但我有另一個層,它增加了一些屬性,進行一些轉換等。所以查詢我的所有數據,轉換它們並將它們返回到WebAPI類進行OData過濾不僅夠好。這當然非常緩慢,通常是一個糟糕的主意。

那麼有沒有辦法讓從我的WebAPI入口點傳播OData查詢參數到我調用的函數來獲取和轉換數據?

例如,GET來/api/people?$skip=10&$top=10會調用服務器上的:

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; 
} 

回答

37

我只是偶然發現了這個舊帖子,我添加了這個答案,因爲它現在很容易處理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; } 
} 
+0

這是最完整/正確的答案,非常感謝,我將在我的下一個項目中使用它。 – frapontillo

+1

看看https://github.com/Roysvork/LinqToQuerystring/,這可以讓您選擇直接從原始odata查詢字符串創建IQueryable <>。 –

+1

謝謝,這表明我朝着正確的方向前進。 –

3

從URL查詢被轉換成一個LINQ表達式樹,然後將其針對您的操作返回的IQueryable執行。您可以分析表達式並以任何您想要的方式提供結果。缺點是你需要實現IQueryable,這不是很容易。如果你感興趣,請看看這篇博客文章系列:http://blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro.aspx。它討論了WCF數據服務,但Web API使用的過濾器表達式非常相似。

2

一種方式隨着Web的API將與客戶消息處理程序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); 
} 
+0

這恐怕是不可行的,因爲數據庫類,並返回類是完全不同的,這是不可能的(因爲某些原因)做轉換在自定義處理程序。 – frapontillo

+0

然後更改響應。上面編輯。 'var persons2 = new List (); //這可以是完全不同的修改模型''response = new HttpResponseMessage >(persons2);' –

+0

這裏最大的問題是你沒有正確地使用IQueryable來過濾它。相反,您正在枚舉完整結果並手動構建新的結果集。這無法使用任何潛在的linq提供者。 –

0

我用WCF Data Services和asp.net mvc 3.5做了這樣的事情,但它有點混亂。

第一步是重寫路徑,以便跳過和頂部選項不會被應用兩次,一次由您和一次由運行時應用。

我用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語句或任何其他分貝命令。我在我的情況下使用NHibernate。我也能夠限制我支持哪些過濾器命令,這使得事情變得更容易。例如,我沒有做分組。大多隻是比較運算符。

OData.org有一個篩選器運算符的列表。將字符串「and」和「or」拆分爲單獨的子句。將每個子句拆分一個空格,您應該得到一個3元素數組,其屬性名稱爲[0]中的運算符[1]和[2]中的值。

這些子句將以Person的形式出現,但我假設你可以將它們轉換成能夠將正確結果從db中提取出來的東西。

我最終放棄了這種方法,因爲它不適用於POSTS。我必須編寫自己的Linq提供程序,但編寫自己的過濾器字符串解析器使其更易於理解。

+0

這可能就是我一直在尋找的東西。謝謝!!!有沒有辦法自動將過濾器應用於IQueryable,還是必須手動執行所有操作? – frapontillo

+1

不幸的是手工。該方法基本上只是將過濾器轉換爲字符串數組。在NH中,這並不是很糟糕,因爲動態構建查詢很容易。 –

+0

'HttpContext.Current.RewritePath(app.Request.Path,「」,queryString);'似乎沒有工作。路徑被改變了,但'ApiController'上面的圖層看起來仍然在處理舊的'$ skip'和'$ top'。你有什麼想法,爲什麼? – frapontillo