Почему Entity Framework генерирует следующие вложенные SQL для контроллеров таблиц Azure Mobile Services
Я пытаюсь понять суть проблемы Framework Framework при использовании TableController
Я создал следующую настройку.
-
В базовом примере TodoItem представлен новый Mobile Web API, который использует EntityFramework, TableController и EntityDomainManager по умолчанию
public class TodoItemController : TableController<TodoItem>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
context = new MobileServiceContext();
context.Database.Log += LogToDebug;
DomainManager = new EntityDomainManager<TodoItem>(context, Request);
}
public IQueryable<TodoItem> GetAllTodoItems()
{
var q = Query();
return q;
}
-
Контроллер ванильного Web API 2.
public class TodoItemsWebController : ApiController
{
private MobileServiceContext db = new MobileServiceContext();
public TodoItemsWebController()
{
db.Database.Log += LogToDebug;
}
public IQueryable<TodoItem> GetTodoItems()
{
return db.TodoItems;
}
Я прошел через код TableController
с тонкой зубной гребенкой, копаясь в методе Query
, который просто проксирует вызов через DomainManager
, чтобы добавить модификацию Where(_ => !_.IsDeleted)
в IQueryable
>
Однако два запроса производят ОЧЕНЬ разные SQL.
Для обычного контроллера веб-API вы получаете следующий SQL.
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Version] AS [Version],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[UpdatedAt] AS [UpdatedAt],
[Extent1].[Deleted] AS [Deleted],
[Extent1].[Text] AS [Text],
[Extent1].[Complete] AS [Complete]
FROM [dbo].[TodoItems] AS [Extent1]
Но для TableController вы получаете следующий фрагмент SQL, в котором есть * Magic * Guid в середине, и приводит к вложению вложенного SQL. Результатом этого является полный мусор, когда вы начинаете общаться с любыми запросами ODATAv3, такими как $top, $skip, $filter и $expand.
SELECT TOP (51)
[Project1].[C1] AS [C1],
[Project1].[C2] AS [C2],
[Project1].[C3] AS [C3],
[Project1].[Complete] AS [Complete],
[Project1].[C4] AS [C4],
[Project1].[Text] AS [Text],
[Project1].[C5] AS [C5],
[Project1].[Deleted] AS [Deleted],
[Project1].[C6] AS [C6],
[Project1].[UpdatedAt] AS [UpdatedAt],
[Project1].[C7] AS [C7],
[Project1].[CreatedAt] AS [CreatedAt],
[Project1].[C8] AS [C8],
[Project1].[Version] AS [Version],
[Project1].[C9] AS [C9],
[Project1].[Id] AS [Id]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Version] AS [Version],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[UpdatedAt] AS [UpdatedAt],
[Extent1].[Deleted] AS [Deleted],
[Extent1].[Text] AS [Text],
[Extent1].[Complete] AS [Complete],
1 AS [C1],
N'804f84c6-7576-488a-af10-d7a6402da3bb' AS [C2],
N'Complete' AS [C3],
N'Text' AS [C4],
N'Deleted' AS [C5],
N'UpdatedAt' AS [C6],
N'CreatedAt' AS [C7],
N'Version' AS [C8],
N'Id' AS [C9]
FROM [dbo].[TodoItems] AS [Extent1]
) AS [Project1]
ORDER BY [Project1].[Id] ASC
Здесь вы можете увидеть результаты обоих запросов. https://pastebin.com/tSACq6eg
Итак, мои вопросы:
-
Почему TableController
генерирует SQL таким образом?
-
Что такое * magic * guid в середине запроса? (он останется неизменным до тех пор, пока я не остановлю и не перезапущу приложение, поэтому я не знаю, зависит ли его сеанс, клиент или контекст БД)
-
Где именно в конвейере находится TableController, делающий эти изменения в IQueryable
? Я предполагаю, что это сделано через некоторый шаг промежуточного программного обеспечения или атрибут execute после запроса после вызова метода Query()
, но я не могу на всю жизнь найти его.
Ответы
Ответ 1
В соответствии с вашим описанием я провел некоторое исследование и обнаружил, что Azure Mobile Server SDK использует следующую строку кода под TableControllerConfigProvider.cs для добавления дополнительных связанные с запросами фильтры для тех же действий с QueryableAttribute для включения действия контроллера для поддержки параметров запроса OData.
controllerSettings.Services.Add(typeof(IFilterProvider), new TableFilterProvider());
Примечание. дополнительные фильтры будут выполняться после того, как ваше действие будет выполнено, и верните IQueryable
.
Вы можете проверить EnableQueryAttribute.cs и обнаружили, что OnActionExecuted
вызывает метод ExecuteQuery
и в конечном итоге вызывает ODataQueryOptions.ApplyTo для применения опций запроса OData ($ filter, $orderby, $top, $skip и $inlinecount и т.д.) к данному IQueryable
.
В моем понимании, вложенный SQL-оператор генерируется компонентом OData. После вызова ODataQueryOptions.ApplyTo
ваш IQueryable был изменен, и связанный с ним оператор sql также был изменен. Я сделал несколько тестов в своем обычном контроллере веб-API следующим образом: вы можете ссылаться на него:
Запрос:
Get http://localhost:58971/api/todoitem?$top=2&$select=Text,Id,Version
Перед применением опций запроса OData:
![введите описание изображения здесь]()
После применения опций запроса OData:
![введите описание изображения здесь]()
Ответ 2
Ваша одна из таблиц синхронизируется между бэкэнд и клиентом, потому что если вы получаете 2-й sql.
Подробнее здесь:
https://documentation.devexpress.com/wpf/17927/Common-Concepts/Scaffolding-Wizard/Tutorials/Building-Outlook-Inspired-and-Hybrid-UI-Applications/Lesson-3-Customize-Layout-of-the-Collection-Views