oData EndPoint: влияет ли это на SQL-запрос?

Я использую OData для своих API.

Хотя мне в целом нравится то, что он может предложить, он использует только данные «Опубликовать мой запрос», что заставляет меня строить все отношения заранее.

Передает ли конечная точка oData с EntityFramework мои параметры oData для выполнения перед моим SQL-запросом?

Прямо сейчас, если я планирую использовать синтаксисы oData, такие как $ Expand, я должен заранее использовать EF Include. И снова проблема в том, что EF должен выстроить все потенциальные отношения, с которыми я могу использовать $ Expand ... даже если я ничего не $ расширяю.

Другой пример: если я использую синтаксис $ top (100). Скажем, у меня было 10000 результатов, EF загрузит все 10000 из БД, а затем OData выберет 100 лучших.

Будет ли конечная точка oData внедряться между EF и БД и в этом случае выбирать только 100 результатов из БД?


person Tom Crosman    schedule 09.02.2020    source источник
comment
Похоже, вы неправильно реализовали свои контроллеры! Пожалуйста, опубликуйте примеры запросов и реализацию вашего контроллера для связанных методов.   -  person Chris Schaller    schedule 13.08.2020


Ответы (2)


Обычно OData и EF идут рука об руку, OData преобразует входящий HTTP-запрос в Выражение Linq-to-Entities, которое EF затем преобразует в выражение SQL.

tl;dr

Все ваши комментарии и наблюдения указывают на неправильную реализацию в вашем контроллере, это звучит подозрительно, как если бы вы следовали примеру, основанному на шаблоне репозитория, а не примерам на основе EF.

Передает ли конечная точка oData с EntityFramework мои параметры oData для выполнения перед моим запросом SQL?

Это именно то, для чего была разработана среда OData, но есть одно предостережение: вы должны настроить свои контроллеры таким образом, чтобы параметры могли передаваться через них.

Есть два механизма, которые позволяют этому случиться. Во-первых, ваш контроллер должен вернуть результат IQueryable<T> (или вы должны передать IQueryable<T> одному из согласованных обработчиков ответов). Во-вторых, вы не должны применять свои собственные выражения фильтра, которые могут противоречить параметрам, в противном случае вы можете привести к тому, что записи не будут возвращены.

Ниже приведен пример двух стандартных Get конечных точек на контроллере OData, которые будут возвращать запрос Vehicle, который позволит передавать выражения _4 _, _ 5_ и $filter:

[ODataRoute]
[HttpGet]
[OData.EnableQuery]
public IHttpActionResult Get(ODataQueryOptions<Vehicle> queryOptions)
{
    return Ok(db.Vehicles);
}

[ODataRoute]
[HttpGet]
[OData.EnableQuery]
public IHttpActionResult Get([FromOdataUri] int key, ODataQueryOptions<Vehicle> queryOptions)
{
    return SingleResultAction(db.Vehicles.Where(x => x.Id == key));
}

Если я вызову это со следующими параметрами запроса:

$filter=Make eq Holden and Model eq Commodore&$orderby=Year desc

тогда это будет преобразовано в запрос SQL, подобный этому:
(SELECT * будет развернут полностью)

DECLARE @make varchar(20) = 'Holden';
DECLARE @model varchar(20) = 'Commodore';

SELECT * 
FROM Vehicle
WHERE Make = @make
  AND Model = @model
ORDER BY Year DESC

Да, параметры тоже будут правильно параметризованы!

Этот простой контроллер также проходит через $expand запрос и автоматически присоединяется к необходимым таблицам, нам вообще не нужно заранее знать или думать о потенциальных включаемых.

Фактически, если я добавлю $skip=5 и $top=2, эти предложения также будут применены непосредственно к оператору SQL, и из базы данных будет возвращено не более 2 строк.


Есть два распространенных сценария, когда все эти автоматические магические переводы запросов и их передача через тупой джамбо нарушаются.

  1. В методе контроллера (для коллекции или элемента) вы не возвращаете результат IQueryable или иным образом выполнили запрос и разрешили его до IEnumerable, а затем вы вернули его, возможно, вернув его обратно к IQueryable .

    • This tends to happen because when we start out we tend follow simple non EF based OData examples, they usually follow a repository pattern because it is simple to explain the model, example data and implementation in a single page of code.

    Объявление общественной службы: не пытайтесь использовать шаблон репозитория для обертывания модели и контекста EF, почти все преимущества EF будут потеряны. EF - это шаблон «Единица работы», OData предназначена для работы с ним напрямую.

    Следующая реализация контроллера НЕ будет передавать параметры запроса в базу данных, _16 _, _ 17 _, _ 18 _, _ 19 _, _ 20_ будет по-прежнему применяться, если это возможно, но только после извлечения всех записей в память в первую очередь. :

    return Ok(db.Vehicles.ToList());
    
    • $expand не будет применяться, или, скорее, это приведет к NULL связанным записям.
  2. Другая распространенная проблема возникает после Действия, когда запись данных (или записи) была обработана, если мы хотим автоматически поддерживать весь диапазон параметров запроса, нам снова нужно убедиться, что мы возвращаем выражение IQueryable, которое запросы от DbContext. Если выражение IQueryable, но запрашивает только то, что уже находится в памяти, то, например, $expand и $filter могут применяться только к тем данным, которые уже загружены.

    • SingleResultAction is a good helper method for returning an IQueryable that has been scoped toa single item, but it will still allow the full range of QueryOptions to be applied.
person Chris Schaller    schedule 22.03.2021

Добро пожаловать в мир боли - Odata - отличная идея с довольно плохой реализацией. Но да, это потрясающе - делаю это сам.

он использует только данные «Опубликовать мой запрос», что заставляет меня заранее построить все взаимосвязи.

Если это спрашивает, что я думаю, что он делает (ваш английский ОЧЕНЬ непонятен), тогда нет, это не так - ваша ошибка заключается в непосредственном открытии объектов ef. У меня есть отдельные объекты api, и я предоставляю их с помощью AutoMapper ProjectTo. Да, мне нужно заранее определить отношения, но не на уровне эф.

Мне нужно использовать EF Include заранее.

Это потому, что вы решили. Я фактически использую, как я уже сказал, Automapper ProjectTo - и динамически получаю необходимые расширения из информации OdataOptions SelectExpand. Нет необходимости отправлять весь граф объекта в EF (что происходит, если вы расширяете все возможные расширения с помощью include), это приведет к действительно плохой производительности. Просто страница программирования, чтобы получить соответствующие включения.

Будет ли конечная точка oData внедряться между EF и БД и в этом случае выбирать только 100 результатов из БД?

если он так запрограммирован, то да. Если кто-то столкнется с проблемой LINQ, запрограммирует его и упакует ef в один из нелепых неэффективных шаблонов репозитория, которые возвращают IENumerable, - тогда нет, он, возможно, все это вытянет. Там видел это. Но обычно TOP (1) приводит к тому, что sql выбирает только первый элемент.

person TomTom    schedule 09.02.2020
comment
Спасибо за ответ. У вас есть ссылка на документацию по «ProjectTo»? Что касается конечной точки OData, я имел в виду это docs.microsoft.com/en-us/aspnet/web-api/overview/ - person Tom Crosman; 10.02.2020
comment
Что ж, он находится в одной из библиотек automapper, и у automapper действительно есть документация. - person TomTom; 10.02.2020
comment
Я посмотрел автокартирование. Кажется, ничем не отличается от кастомных классов? oData заставляет вас возвращать запрашиваемый тип: / - person Tom Crosman; 10.02.2020
comment
Ах, это на самом деле ОЧЕНЬ другое, если вы потрудитесь написать пример. Помимо прочего, он позволяет централизовать сопоставления и повторное использование, что ОЧЕНЬ удобно при выполнении кросс-контроллеров (потому что вам нужен один объект с другого контроллера). - person TomTom; 10.02.2020
comment
Automapper полезен, если вы НЕ собираетесь открывать объекты EF напрямую через модель OData, однако OData уже отображает запрос на EF и обратно, поэтому для многих приложений автоматическое отображение на самом деле может потребовать больше настроек и обработки, чем если бы вы просто использовали EF для OData напрямую. - person Chris Schaller; 22.03.2021