Почему 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:

введите описание изображения здесь