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

Posted

技术标签:

【中文标题】OData 错误:URI 中指定的查询无效。该属性不能在查询选项中使用【英文标题】:OData Error: The query specified in the URI is not valid. The property cannot be used in the query option 【发布时间】:2017-01-23 17:20:20 【问题描述】:

我正在尝试启动 OData 端点并使其正常工作,但我遇到了这个错误,即使是 Google 也无话可说。

我创建了一个实体框架 EDMX 上下文(首先是数据库),让设计人员从中生成 2 个模型。

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

我可以做到这一点:

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

检索主 ID 为 6587660 的项目。

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

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

将失败并出现以下错误:

URI 中指定的查询无效。 $filter 查询选项中不能使用属性“ProjectID”。

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

我检查了 EF 生成的模型在属性上没有任何属性,它们没有。

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

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 与 Database First 结合使用,所以我不确定是什么原因造成的。

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

【问题讨论】:

会不会是物业的情况?你试过$filter=projectId eq 6587660$filter=projectID eq 6587660 吗? @Igor 我试过了。任何其他大小写都会返回一个错误,说明该属性不存在,所以我的大小写是正确的。 您是否尝试过 $filter 使用模型上的另一个属性(如字符串属性)? 您是否启用(或未设置)任何these options,它会阻止您使用$filter?目前这是很多猜测和问题,您应该提供一些代码,例如您正在使用的配置和 odata 控制器,以提供更多信息。 我遇到了完全相同的问题。我实际上不能使用任何查询语法参数,$count、$filter、$select 等。它们都失败了。 【参考方案1】:

来自the docs 13.1 Model Bound Attributes:

现在 WebAPI OData 的默认设置是:client can't apply $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");

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

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

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

这个答案应该可以解决您发布的问题,但我希望您需要查看 those docs 以使您能够为项目的其余部分制定一个全面的解决方案(当然,除非您只需部署一条线就可以了!)。


正如“模型绑定属性”的名称所暗示的那样,您还可以通过模型上的属性来实现您所需要的,这也包含在the docs 中(实际上也是主要关注点)。


2017 年 2 月编辑:

per-entity fluent API 中似乎存在错误。尽管实体集是使用流畅的 API 设置的,但对 $expand 实体集的调用会间歇性地返回 400 错误请求,并显示原始问题中的错误。我不知道这个错误是否只存在于$expand 或其他查询参数中。我也不知道是我的代码导致了问题还是 MS 错误,因此其他人也遇到了问题。我将很快对此进行进一步调查并更新此答案。现在我正在使用单行捕获所有;效果很好。

进一步编辑:

我刚刚重读了一些the docs(试图让这个更新尽可能容易理解),它们似乎暗示我现在的设置方式(使用全局配置单线捕获所有加上fluent API),仍然会尊重per-entity fluent API,因为:

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

因此,也许这就是您必须做的:添加一条线-全部捕获,然后使用模型绑定属性、流式 API 或两者进行微调。我需要对此进行测试,并会尽快报告...

【讨论】:

突破性变化好吧。这有点打破了我对使用 MS 的 OData 实现的兴趣。还是非常感谢。 @FrancisDucharme 不要对变化过于失望;这是一个很好的安全/性能增强功能,如果您不需要细粒度的控制,则可以解决所有问题。乐意效劳。 :-) @lukkea 我确信 SO 问题会有所帮助,但如果 OData 团队中的任何人在倾听,那么更好的错误消息也会大大有助于将我指向正确的方向。 如何获得“config”对象?这是哪里来的?我正在使用 .NET 核心,但似乎看不到等效功能。 @NickG 看看这个问题 - 这就是我从中获取配置对象的地方。【参考方案2】:

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

private static IEdmModel GetEdmModel()

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

【讨论】:

以上是关于OData 错误:URI 中指定的查询无效。该属性不能在查询选项中使用的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 为 OData 查询中指定的每个过滤器获取一组键/值对?

ActivityTransitionRequest 中指定的活动无效

在iPod上部署时出现“无效的权利”错误

AZURE 表存储、ODATA 和更友好的 URI 查询

有效负载中指定的无效资源的 HTTP 状态代码

指定了一个无效的目标地址