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

Fra*_*rme 37 .net c# entity-framework odata asp.net-web-api

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

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

除了$filter查询失败之外,一切正常.

我可以做得很好:

http://localhost:27164/Projects(6587660)
Run Code Online (Sandbox Code Playgroud)

它检索主ID为6587660的Project.

但任何$filter要求如下:

http://localhost:27164/Projects?$filter=ProjectID eq 6587660
Run Code Online (Sandbox Code Playgroud)

将失败并出现以下错误:

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()
    );           

}
Run Code Online (Sandbox Code Playgroud)

这是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我第一次使用OData和Database First,所以我不确定是什么导致了这一点.

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

luk*_*kea 114

来自文档13.1模型绑定属性:

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

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

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

但这是一个包罗万象的工作,围绕着这种变化带来的更好的安全性/性能.

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

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

这个答案可以解决您发布的问题,但是,我希望您需要查看这些文档,以便为项目的其余部分提供全面的解决方案(当然,除非您只是部署一个-line catch all!).


正如名称"模型绑定属性"所暗示的那样,您还可以通过模型上的属性实现所需的内容,这也是文档中的(实际上也是主要关注点).


2017年2月编辑:

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

进一步编辑:

我刚刚重读了一些文档(试图尽可能地理解这个更新),它们似乎暗示我现在设置的方式(使用Global Config one-line-catch-all加上流畅的API) ,每个实体的流畅API仍将受到尊重,因为:

"查询设置可放置在许多地方,从最低到最高的优先级如下:系统默认值(默认情况下不可查询),全局配置,模型绑定属性,Fluent API."

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

  • 打破变化好吧.这有点让我对使用MS的OData实现感兴趣.非常感谢. (4认同)
  • @FrancisDucharme不要太改变; 这是一个很好的安全/性能增强,如果你不需要粒度控制,那就是全能解决方案.乐意效劳.:-) (3认同)
  • @lukkea我确信SO问题会有所帮助,但是如果OData团队中的任何人正在听,那么更好的错误消息也将大大有助于我指出正确的方向。 (2认同)