繁体   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