Подтвердить что ты не робот

Ошибка OData: запрос, указанный в URI, недопустим. Свойство не может использоваться в опции запроса

Я пытаюсь получить конечную точку OData и работать, и у меня есть эта ошибка, о которой даже Google не может многое сказать.

Я создал контекст Entity Framework EDMX (сначала база данных), если разработчик создал из него 2 модели.

Все работает нормально, за исключением ошибок $filter.

Я могу сделать это хорошо:

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

Возвращает проект с первичным идентификатором 6587660.

Но любые запросы $filter как таковые:

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

Ошибка со следующей ошибкой:

Запрос, указанный в URI, недействителен. Свойство "ProjectID" не может использоваться в опции запроса $filter.

Я также пробовал запрашивать другие свойства, свойства строки тоже. Такая же ошибка.

Я проверил, что модель, сгенерированная EF, не имеет атрибутов свойств, они этого не делают.

Здесь мой метод Register в модуле 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()
    );           

}

Здесь контроллер проектов (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 с базой данных First, поэтому я не уверен, что это вызывает.

Я использую последние версии от Nuget на .NET 4.5.2.

4b9b3361

Ответ 1

Из docs 13.1 Связанные атрибуты модели:

Теперь значение по умолчанию для WebAPI OData: клиентский подход $count, $orderby, $select, $top, $expand, $filter в запросе, запрос например localhost\odata\Customers? $orderby = Имя не будет выполнено BadRequest, поскольку по умолчанию все свойства не сортируются по умолчанию. является изменением в 6.0.0

Итак, теперь нам нужно включить атрибуты Bound Aound, которые вы можете выполнить глобально, со средней линией в следующем блоке (остальные два - ваш код):

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

Но это все-таки всевозможные работы вокруг лучшей безопасности/производительности, которые это изменение приносит.

Итак, вы можете и, возможно, должны включить атрибуты OData Bound Attributes, используя бесплатные API-вызовы для каждого объекта следующим образом:

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

Этот ответ должен решить проблему, о которой вы писали, но, я думаю, вам нужно будет взглянуть на те документы, чтобы вы могли разработайте комплексное решение для остальной части вашего проекта (если, конечно, вы просто не разблокируете однострочный улов!).


Как указывает название "Связанный с моделью атрибут", вы также можете добиться того, что вам нужно, используя атрибуты ваших моделей, на которые распространяется (на самом деле, основное внимание) документы.


Изменить февраль 2017:

Кажется, что ошибка в API-интерфейсе для каждого объекта существует. Вызов $expand наборов объектов периодически прерывает 400 Bad Request с ошибкой в ​​исходном вопросе, несмотря на то, что набор объектов настраивается с использованием свободного API. Я не знаю, существует ли эта ошибка только в $expand или с другими параметрами запроса. Я также не знаю, вызывает ли это мой код проблему или ошибку MS, и поэтому что-то еще встречается. Я буду исследовать это в ближайшее время и обновить этот ответ. Пока я использую однострочный улов; это прекрасно работает.

Дальнейшее редактирование:

Я просто перечитал некоторые документы (чтобы попытаться получить это обновление как можно более понятное), и они, похоже, подразумевают, что путь Теперь у меня есть настройки (с помощью универсального API-интерфейса Global Config one-line-catch-all plus fluent API), свободный API для каждого объекта будет по-прежнему соблюдаться, потому что:

"Настройки запроса могут быть размещены во многих местах, со следующими приоритет от самого низкого до высшего: System Default (не запрашивается по умолчанию), Глобальная конфигурация, Атрибут привязки модели, Свободный API."

Поэтому, возможно, это то, что вам нужно сделать: добавить однострочный-поймать, а затем точно настроить атрибуты, связанные с моделью, свободный API или и то, и другое. Мне нужно проверить это и скоро отчитаться...

Ответ 2

Чтобы ответить на вопрос, заданный @NickG и др.: В .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();
}