Erreur OData: la requête spécifiée dans L'URI n'est pas valide. La propriété ne peut pas être utilisée dans l'option de requête

J'essaie d'obtenir un point de terminaison OData et de travailler et j'ai cette erreur que même Google n'a pas grand-chose à dire.

J'ai créé un contexte Entity Framework EDMX (base de données en premier), le concepteur en a généré 2 modèles.

Tout fonctionne bien, sauf que les requêtes $filter échouent.

Je peux le faire très bien:

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

Qui récupère le projet avec un identifiant principal de 6587660.

Mais toutes les requêtes $filter en tant que telles:

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

Volonté échouer avec l'erreur suivante:

La requête spécifiée dans L'URI n'est pas valide. La propriété 'ProjectID' ne peut pas être utilisée dans l'option de requête $ filter.

J'ai également essayé d'interroger d'autres propriétés, des propriétés de chaîne aussi. Même erreur.

J'ai vérifié que le modèle généré par EF n'a pas d'attributs sur les propriétés, ils ne le font pas.

Voici ma méthode de Registre dans WebApiConfig.module 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()
    );           

}

Voici le contrôleur de projets (GetProjects est la méthode appelée lors d'une requête $ 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;
    }
}

C'est la première fois que J'utilise OData avec la base de données en premier, donc je ne suis pas sûr de ce qui cause cela.

J'utilise les derniers runtimes de Nuget sur. NET 4.5.2.

27
demandé sur lukkea 2016-09-15 18:42:20

1 réponses

À partir de les attributs liés au modèle docs 13.1 :

Maintenant, le paramètre par défaut pour WebAPI OData est: client can't apply $count, $orderby, $select, $top, $expand, $filtre dans la requête, la requête comme localhost \ odata \ clients?$ orderby = Name échouera comme BadRequest, parce que toutes les propriétés ne sont pas triables par défaut, cette est un changement de rupture dans 6.0.0

Donc, nous devons maintenant activer les attributs liés au Modèle OData que vous peut fais globalement avec la ligne médiane dans le bloc suivant (les deux autres sont votre code):

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

Mais c'est un fourre-tout et une sorte de travail autour de la meilleure sécurité/performance que ce changement apporte.

Ainsi, vous pouvez, et devriez peut-être, activer les attributs liés au Modèle OData en utilisant des appels D'API courants par entité comme ceci:

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

Cette réponse devrait résoudre le problème que vous avez posté mais, je m'attends à ce que vous deviez jeter un oeil à ces documents pour vous permettre de élaborez une solution complète pour le reste de votre projet (à moins, bien sûr, de déployer le catch all d'une seule ligne!).


Comme le suggère le nom "attribut lié au modèle", vous pouvez également réaliser ce dont vous avez besoin via des attributs sur vos modèles, qui sont couverts dans (en fait, est l'objectif principal de) Les docs aussi.


Modifier Février 2017:

Il semble y avoir un bogue dans l'API fluent par entité. Les appels à $expand entity-sets renvoient par intermittence un 400 mauvaise requête avec l'erreur dans la question d'origine malgré la configuration des ensembles d'entités avec L'API fluent. Je ne sais pas si ce bug n'existe que sur $expand ou avec d'autres paramètres de requête. Je ne sais pas non plus si c'est mon code qui cause le problème ou un bogue MS et donc quelque chose que d'autres rencontrent. Je vais enquêter plus loin bientôt et mettre à jour cette réponse. Pour l'instant, j'utilise le catch all d'une ligne; cela fonctionne très bien.

Plus Modifier:

Je je viens de relire une partie de les documents (pour essayer d'obtenir cette mise à jour aussi compréhensible que possible) et ils semblent impliquer que la façon dont j'ai maintenant mis en place les choses (avec la configuration globale une ligne-catch-all Plus fluent API), l'API fluent par entité sera toujours respectée car:

" les paramètres de requête peuvent être placés dans de nombreux endroits, avec les éléments suivants priorité du plus bas au plus haut: défaut du système (non interrogeable par par défaut), Configuration globale, attribut lié au Modèle, API Fluent."

Par conséquent, c'est peut-être ce que vous devez faire: ajouter le catch-all d'une ligne, puis affiner avec les attributs liés au modèle, l'API fluide ou les deux. Je dois tester cela et je ferai bientôt rapport...

97
répondu lukkea 2017-02-13 13:18:43