web-dev-qa-db-ja.com

ODataエラー:URIで指定されたクエリが無効です。このプロパティはクエリオプションでは使用できません

私はODataエンドポイントを立ち上げて動作させようとしているのですが、このエラーが発生しているので、グーグルでさえあまり語ることはできません。

Entity Framework EDMXコンテキスト(データベースが最初)を作成し、デザイナーに2つのモデルを生成させました。

$filterクエリが失敗する場合を除き、すべてが正常に機能しています。

私はこれをうまく行うことができます:

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

6587660のプライマリIDでプロジェクトを取得します。

ただし、$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;
    }
}

Database FirstでODataを使用するのはこれが初めてなので、何が原因なのかわかりません。

.NET 4.5.2上のNugetの最新のランタイムを使用しています。

37

ドキュメント13.1モデルバインド属性

WebAPI ODataのデフォルト設定は次のとおりです。クライアントはクエリで$ count、$ orderby、$ select、$ top、$ expand、$ filterを適用できません。localhost\ odata\Customers?$ orderby = Nameのようなクエリは失敗しますBadRequest、すべてのプロパティはデフォルトでソートできないため、これは6.0.0の重大な変更です

そのため、OData Model Bound Attributesを有効にする必要があります。これは、can中央の行でグローバルに行うことができます次のブロック(他の2つはコードです):

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

この回答は、あなたが投稿した問題を解決するはずですが、プロジェクトの残りの部分について包括的なソリューションを作成できるようにするには、 これらのドキュメント を確認する必要があります。もちろん、1行ですべてをキャッチするだけです!)。


「Model Bound Attribute」という名前が示唆するように、モデルの属性を介して必要なものを実現することもできます。これは(実際、主な焦点です) ドキュメント でも説明されています。


2017年2月編集:

エンティティごとに流APIなAPIにはバグがあるようです。 $expandエンティティセットの呼び出しは、エンティティセットがFluent APIでセットアップされているにもかかわらず、元の質問にエラーがある400 Bad Requestを断続的に返します。このバグが$expandにのみ存在するのか、他のクエリパラメーターにのみ存在するのかはわかりません。また、問題を引き起こしているのが自分のコードなのか、MSバグなのか、したがって他の人が遭遇しているのかどうかもわかりません。すぐにこれをさらに調査し、この回答を更新します。今のところ、私は1行のキャッチオールを使用しています。それはうまく機能します。

さらに編集:

docs (この更新をできるだけ理解できるようにするため)の一部を読み直したところ、(Global Config one-line-キャッチオールプラス流APIなAPI)、エンティティごとの流APIなAPIは引き続き尊重されます。

「クエリ設定は多くの場所に配置できます。優先度は最低から最高の順に設定されています:システムデフォルト(デフォルトではクエリ不可)、グローバル構成、モデルバウンド属性、Fluent API。」

したがって、多分これはあなたがしなければならないことです:one-line-catch-allを追加してから、model-bound-attributes、fluent API、またはその両方で微調整します。これをテストする必要があり、すぐに報告します...

113
lukkea

@NickG et al:.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();
}
0
realbart