it-swarm.com.de

OData-Fehler: Die in der URI angegebene Abfrage ist ungültig. Die Eigenschaft kann nicht in der Abfrageoption verwendet werden

Ich versuche, einen OData-Endpunkt zum Laufen zu bringen, und habe diesen Fehler, zu dem selbst Google nicht viel zu sagen hat.

Ich habe einen Entity Framework EDMX-Kontext (Datenbank zuerst) erstellt und vom Designer 2 Modelle daraus generieren lassen.

Alles funktioniert gut, außer $filter Abfragen schlagen fehl.

Ich kann das gut machen:

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

Welches ruft das Projekt mit einer primären ID von 6587660.

Aber jeder $filter Anfragen als solche:

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

Scheitert mit folgendem Fehler:

Die in der URI angegebene Abfrage ist ungültig. Die Eigenschaft 'ProjectID' kann in der Abfrageoption $ filter nicht verwendet werden.

Ich habe auch versucht, andere Eigenschaften abzufragen, Zeichenfolge-Eigenschaften auch. Gleicher Fehler.

Ich habe überprüft, dass das von EF generierte Modell keine Attribute für die Eigenschaften hat, sie nicht.

Hier ist meine Register-Methode im WebApiConfig.cs-Modul:

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

}

Hier ist der Projects-Controller (GetProjects ist die aufgerufene Methode, wenn eine $ filter-Abfrage durchgeführt wird):

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;
    }
}

Dies ist das erste Mal, dass ich OData mit Database First verwende. Ich bin mir also nicht sicher, was dies verursacht.

Ich verwende die neuesten Laufzeiten von Nuget unter .NET 4.5.2.

37

Von in den Dokumenten 13.1 Model Bound Attributes :

Die Standardeinstellung für WebAPI-OData lautet nun: Client kann $ count, $ orderby, $ select, $ top, $ expand, $ filter in der Abfrage nicht anwenden, Abfrage wie localhost\odata\Customers? $ Orderby = Name ist fehlgeschlagen als BadRequest: Da nicht alle Eigenschaften standardmäßig sortiert werden können, ist dies eine grundlegende Änderung in 6.0.0

Deshalb müssen wir jetzt OData Model Bound-Attribute aktivieren, die Sie können im Folgenden global mit der Mittellinie ausführen block (die anderen beiden sind dein Code):

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

Aber das ist ein Allheilmittel, um die bessere Sicherheit/Leistung, die diese Änderung bringt, zu umgehen.

Daher können und sollten Sie OData Model Bound-Attribute mithilfe fließender API-Aufrufe pro Entität wie folgt aktivieren:

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

Diese Antwort sollte das Problem lösen, über das Sie geschrieben haben, aber ich gehe davon aus, dass Sie sich diese Dokumente ansehen müssen, damit Sie eine umfassende Lösung für den Rest Ihres Projekts ausarbeiten können (es sei denn, Natürlich setzen Sie nur die einzeilige catch all!).


Wie der Name "Model Bound Attribute" andeutet, können Sie das, was Sie benötigen, auch über Attribute für Ihre Modelle erreichen, die in the docs ebenfalls behandelt werden.


Edit Februar 2017:

Es scheint einen Fehler in der pro Entität fließenden API zu geben. Aufrufe von $expand Entity-Sets geben zeitweise eine 400 Bad Request mit dem Fehler in der ursprünglichen Frage zurück, obwohl die Entity-Sets mit der fluent API eingerichtet wurden. Ich weiß nicht, ob dieser Fehler nur bei $expand Oder bei anderen Abfrageparametern auftritt. Ich weiß auch nicht, ob es mein Code ist, der das Problem oder einen MS-Fehler verursacht und daher auf etwas anderes stößt. Ich werde dies bald weiter untersuchen und diese Antwort aktualisieren. Im Moment benutze ich die Einzeilen-Funktion catch all. das funktioniert gut.

Weiter bearbeiten:

Ich habe gerade einige von den Dokumenten (um zu versuchen, dieses Update so verständlich wie möglich zu machen) erneut gelesen und sie scheinen zu implizieren, dass die Art und Weise, wie ich die Dinge jetzt eingerichtet habe (mit der globalen Konfiguration einzeilig) catch-all plus fließende API), die fließende API pro Entität wird weiterhin eingehalten, weil:

"Abfrageeinstellungen können an vielen Stellen abgelegt werden, mit der folgenden Rangfolge von der niedrigsten zur höchsten: Systemstandard (standardmäßig nicht abfragbar), Globale Konfiguration, Modellgebundenes Attribut, Fließende API."

Vielleicht müssen Sie deshalb Folgendes tun: Fügen Sie das One-Line-Catch-All hinzu und optimieren Sie es dann mit modellgebundenen Attributen, flüssiger API oder beidem. Ich muss das testen und melde mich bald wieder ...

113
lukkea

Um die von @NickG et al. Gestellte Frage zu beantworten: In .Net Core machen Sie etwas Ähnliches:

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