Ошибка 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.
Ответы
Ответ 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();
}