OData错误:URI中指定的查询无效。该属性不能在查询选项中使用

时间:2016-09-15 15:42:21

标签: c# .net entity-framework asp.net-web-api odata

我试图让OData端点启动并正常工作,我发现这个错误,即使谷歌也没什么可说的。

我已经创建了一个Entity Framework EDMX上下文(数据库优先),让设计师从中生成2个模型。

一切正常,但$filter查询失败。

我可以做得很好:

http://localhost:27164/Projects(6587660)

使用主ID为6587660检索项目。

但是任何$filter请求都是这样的:

http://localhost:27164/Projects?$filter=ProjectID eq 6587660

将因以下错误而失败:

  

URI中指定的查询无效。物业' ProjectID'不能在$ filter查询选项中使用。

我也尝试过查询其他属性,字符串属性。同样的错误。

我已经检查过EF生成的模型在属性上没有任何属性,他们不会。

这是我在WebApiConfig.cs模块中的Register方法:

using System.Web.OData.Builder;
using System.Web.OData.Extensions;

public static void Register(HttpConfiguration config)
{
    // Web API configuration and services
    // Configure Web API to use only bearer token authentication.
    config.SuppressDefaultHostAuthentication();
    config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));


    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<DB.Project>("Projects");
    config.MapODataServiceRoute(
        routeName: "ODataRoute",
        routePrefix: null,
        model: builder.GetEdmModel()
    );           

}

这里是Projects控制器(GetProjects是进行$ filter查询时的被调用方法):

public class ProjectsController : ODataController
{
    private AppContext db = new AppContext();

    //I've tried decorating with that: [EnableQuery(AllowedQueryOptions = System.Web.OData.Query.AllowedQueryOptions.All, AllowedArithmeticOperators = System.Web.OData.Query.AllowedArithmeticOperators.All)] and no go
    [EnableQuery]
    public IQueryable<Project> GetProjects()
    {
        return db.Projects;
    }

    // GET: odata/Projects(5)
    [EnableQuery]
    public SingleResult<Project> GetProject([FromODataUri] int key)
    {
        return SingleResult.Create(db.Projects.Where(project => project.ProjectID == key));
    }

    /*
    // PUT: odata/Projects(5)
    public IHttpActionResult Put([FromODataUri] int key, Delta<Project> patch)
    {
        Validate(patch.GetEntity());

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        Project project = db.Projects.Find(key);
        if (project == null)
        {
            return NotFound();
        }

        patch.Put(project);

        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!ProjectExists(key))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return Updated(project);
    }

    // POST: odata/Projects
    public IHttpActionResult Post(Project project)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        db.Projects.Add(project);
        db.SaveChanges();

        return Created(project);
    }

    // PATCH: odata/Projects(5)
    [AcceptVerbs("PATCH", "MERGE")]
    public IHttpActionResult Patch([FromODataUri] int key, Delta<Project> patch)
    {
        Validate(patch.GetEntity());

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        Project project = db.Projects.Find(key);
        if (project == null)
        {
            return NotFound();
        }

        patch.Patch(project);

        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!ProjectExists(key))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return Updated(project);
    }

    // DELETE: odata/Projects(5)
    public IHttpActionResult Delete([FromODataUri] int key)
    {
        Project project = db.Projects.Find(key);
        if (project == null)
        {
            return NotFound();
        }

        db.Projects.Remove(project);
        db.SaveChanges();

        return StatusCode(HttpStatusCode.NoContent);
    }
    */

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            db.Dispose();
        }
        base.Dispose(disposing);
    }

    private bool ProjectExists(int key)
    {
        return db.Projects.Count(e => e.ProjectID == key) > 0;
    }
}

这是我第一次将OData与数据库优先使用,所以我不确定导致这种情况的原因。

我在.NET 4.5.2上使用Nuget的最新运行时。

2 个答案:

答案 0 :(得分:111)

来自the docs 13.1 Model Bound Attributes

  

现在,WebAPI OData的默认设置是:客户端无法应用   $ count,$ orderby,$ select,$ top,$ expand,$ filter查询,查询   像localhost \ odata \ Customers?$ orderby =名称将失败为   BadRequest,因为默认情况下所有属性都不可排序,这个   是6.0.0的重大变化

因此,我们现在需要启用OData模型绑定属性, 可以 使用以下块中的中间行进行全局操作(另外两个是您的代码):

ODataModelBuilder builder = new ODataConventionModelBuilder();
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null); //new line
builder.EntitySet<DB.Project>("Projects");

但这是一个包罗万象的方式,可以解决这一变化带来的更好的安全性/性能。

因此,你可以,也许应该使用每个实体使用流畅的API调用启用OD​​ata模型绑定属性,如下所示:

builder.EntitySet<DB.Project>("Projects"); //your line of code
builder.EntityType<DB.Project>().Filter("ProjectID");

这个答案可以解决您发布的问题,但我希望您需要查看those docs,以便为项目的其余部分制定全面的解决方案(当然,除非,你只需要部署单行捕获!)。

正如名称“模型绑定属性”所暗示的那样,您还可以通过模型上的属性实现所需的内容,这也是the docs所涵盖的(事实上,也是主要关注点)。

2017年2月编辑:

每个实体的流畅API似乎存在错误。尽管使用流畅的API设置了实体集,但对$expand实体集的调用会间歇性地返回400 Bad Request并在原始问题中出现错误。我不知道这个错误是仅存在于$expand还是存在于其他查询参数中。我也不知道是我的代码是导致问题还是MS错误,因此是其他人遇到的问题。我将很快对此进行调查并更新此答案。现在我正在使用单线捕捉全部;效果很好。

进一步修改:

我刚刚重读了the docs的一些内容(尝试让这个更新尽可能理解),它们似乎意味着我现在设置的方式(使用Global Config one-line-catch) -all加上流畅的API),每个实体的流畅API仍将受到尊重,因为:

  

“查询设置可以放在很多地方,具体如下   从最低到最高的优先级:系统默认值(不能查询   默认),全局配置,模型绑定属性,Fluent API。“

因此,也许这就是你要做的事情:添加one-line-catch-all然后使用模型绑定属性,流畅API或两者进行微调。我需要对此进行测试并尽快报告...

答案 1 :(得分:0)

要回答@NickG等人提出的问题: 在.Net Core中,您可以执行类似的操作:

private static IEdmModel GetEdmModel()
{
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    var products = builder.EntitySet<Product>("Products");
    products.EntityType.Count().Filter().OrderBy().Expand().Select();
    return builder.GetEdmModel();
}