簡體   English   中英

ASP .NET MVC 4 WebApi:手動處理OData查詢

[英]ASP .NET MVC 4 WebApi: Manually handle OData queries

我有一個使用ASP .NET MVC 4提供的WebAPI制作的Web服務。我知道WebAPI工作之上的層自動處理OData查詢 (例如$filter$top$skip ),但是如果我想要的話自己處理過濾?

我不是簡單地從我的數據庫返回數據 ,但我有另一個層添加了一些屬性,進行了一些轉換等等。因此查詢我的所有數據,轉換它們並將它們返回到WebAPI類進行OData過濾不僅僅是好的足夠。 它當然非常慢,通常是一個糟糕的想法。

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

例如,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;
}

我只是偶然發現了這個老帖子,我正在添加這個答案,因為現在很容易自己處理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; }
}

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

使用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);
 }

我使用WCF數據服務和asp.net mvc 3.5做了類似的事情,但它有點像kludge。

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

我用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”拆分為單獨的子句。 用空格分割每個子句,你應該得到一個3元素數組,其中[0]中的屬性名稱為[1]中的運算符,[2]中的值。

這些條款將以Person為單位,但我假設您將能夠將它們轉換為可以從db中獲取正確結果的內容。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM