57

我有一个使用 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;
}
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 数据服务,但 Web API 使用的过滤器表达式将非常相似。

于 2012-05-28T09:12:20.870 回答
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);
 }
于 2012-05-28T10:07:31.080 回答
0

我用 WCF 数据服务和 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 语句或任何其他 db 命令。在我的案例中,我使用的是 NHibernate。我还能够限制我支持的过滤器命令,这使事情变得更容易。例如,我没有进行分组。主要是比较运算符。

OData.org上有一个过滤器运算符列表。通过“and”和“or”将字符串拆分为单独的子句。用空格分隔每个子句,您应该得到一个 3 元素数组,其中属性名称在 [0] 中,运算符在 [1] 中,值在 [2] 中。

这些子句将以 Person 的形式出现,但我假设您将能够将它们转换为可以从数据库中提取正确结果的内容。

我最终放弃了这种方法,因为它不适用于 POSTS。我必须编写自己的 Linq 提供程序,但编写自己的过滤字符串解析器使这更容易理解。

于 2012-05-28T12:24:53.557 回答