Расширение свойств навигации с помощью ODataQueryOptions

Я создаю веб-сервис OData v.4, который должен предоставлять данные, полученные из другого стороннего веб-источника, поэтому данные не похожи ни на что из мира LINQ, то есть: нет IQueryable, нет контекста, нет, что угодно..

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

class MyMasterEntity {
    [Contained]
    public IEnumerable<MyDetailEntity> Details { get; set; }
}

// [EnableQuery]
public IEnumerable<MyMasterEntity> Get(ODataQueryOptions<MyMasterEntity> options)
{
   // process .FilterQueryOption
   // process .SelectExpandQueryOption
   // process .SkipQueryOption
   // process .TopQueryOption
   return myMasterEntityList;
}

Это хорошо работает за исключением $expand=Details, в этом случае свойства не раскрываются в результате ответа, хотя моя логика добавляет их просто отлично.

Если добавить [EnableQuery] атрибута (который не имеет смысла начинать, так как он является взаимоисключающим со всей идеей ODataQueryOptions), тогда расширение начинает работать. Или скорее притворяется, что работает, потому что на самом деле происходит то, что запрос обрабатывается дважды: сначала моим кодом, затем механизмом OData после того, как я вернул данные. Это может быть допустимо (во всяком случае, я делаю дорогие вызовы вручную, поэтому нет ничего особенного в повторных попытках OData на уже подготовленных данных), если бы не тот факт, что второй проход привел к недетерминированным операциям, таким как $skip. (То есть можно применять $top столько раз, сколько вы хотите получить один и тот же результат, но делать это с $ skip неправильно).

Как я понял из релевантных сборок для обратного инжиниринга, стандартный код расширения оборачивает сущности во что-то, что указывает формататору JSON выдавать соответствующие свойства, независимо от того, действительно ли они раскрыты внутри сущностей.

Также попробовал:

  • изменение типа возврата (IQueryable, IHttpActionResult)
  • принудительный вызов SelectExpandQueryOption.ApplyTo(myMasterEntityList, new ODataQuerySettings()) после ручного расширения, но перед возвратом

Как правильно расширить свойства навигации?

2 ответа

Решение

Я считаю, что вам нужно ввести кусок кода следующим образом

if(options.SelectExpand != null)
{
    Request.ODataProperties().SelectExpandClause = options.SelectExpand.SelectExpandClause;
}

Метод расширения ODataProperties определяется в пространстве имен System.Web.OData.Extensions,

Это говорит форматеру OData визуализировать также select а также expand выводить. В противном случае он не может коснуться расширенных свойств, поскольку может вызвать перечисление. По крайней мере, так я это понимаю.

В противном случае, похоже, ваш подход является правильным. У меня работает, так сказать. Мой случай заключается в том, что у меня есть первая модель базы данных, которую я не могу дополнить (например, отношения между представлениями), поэтому я дополняю модель OData и, например, expand вручную "ссылки на внешние ключи" и отношения "один ко многим", иначе не в заявленной модели edmx. Затем, когда это возможно, я бы хотел, чтобы запрос OData достиг уровня БД (например, filter), так что я делаю примерно так

public async Task<IHttpActionResult> Get(ODataQueryOptions<T> options)
{
    IQueryable<T> tempQuery = initialQuery; //E.g. EfContext.T
    IQueryable<T result = tempQuery;

    if(options.SelectExpand != null)
    {
        if(options.Filter != null)
        {
            tempQuery = options.Filter.ApplyTo(tempQuery, new ODataQuerySettings()) as IQueryable<T>;   
        }
        /* Other options that should go to the DB level... */

        //The results need to be materialized, or otherwise EF throws a
        //NotSupportedException due to columns and properties that aren't in the DB...
        result = (await tempQuery.ToListAsync()).AsQueryable();

        //Do here the queries that can't be hit straight by the queries. E.g.
        //navigation properties not defined in EF views (that can't be augmented)...

        //This is needed to that OData formatter knows to render navigation links too.
        Request.ODataProperties().SelectExpandClause = options.SelectExpand.SelectExpandClause;
}

return Ok(result);

Почему вы хотите обработать параметры запроса вручную?

Использовать AsQueryable Метод расширения LINQ для преобразования ваших данных в запрашиваемую коллекцию (фактически вы используете LINQ to Object).

  • Ваш контроллер должен наследовать от ODataController
  • IEdmModel должен быть связан с маршрутом. Определите его с помощью ODataModelBuilder (или ODataConventionModelBuilder).
  • Ваш метод должен вернуться IQueryable<MyMasterEntity>
  • Объявить только [EnableQuery], Не объявляйте явное ODataQueryOptions аргумент
Другие вопросы по тегам