为什么 Entity Framework 为 Azure 移动服务 Table 控制器生成以下嵌套 SQL

Why is Entity Framework generating the following nested SQL for Azure Mobile Services Table Controllers

当我将它与 TableController

一起使用时,我正试图弄清 entity Framework 问题的根源

我创建了以下设置。

  1. 基本的 TodoItem 示例提供了一个新的移动 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;
        }
    
  2. 香草网 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。

对于常规 Web 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

所以我的问题是:

您的其中一张表正在后端和客户端之间同步,因为如果那样您将获得第二个 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

根据您的描述,我做了一些研究,发现 Azure 移动服务器 SDK 在 TableControllerConfigProvider.cs for adding additional query related filters for the same actions with the QueryableAttribute 下使用以下代码行来启用控制器操作以支持 OData 查询参数。

controllerSettings.Services.Add(typeof(IFilterProvider), new TableFilterProvider());

注意: 其他过滤器将在您的操作执行后执行,return IQueryable

您可以检查 EnableQueryAttribute.cs and found that OnActionExecuted would call the ExecuteQuery method and eventually call ODataQueryOptions.ApplyTo 以将 OData 查询选项($filter、$orderby、$top、$skip 和 $inlinecount 等)应用到给定的 IQueryable

据我了解,嵌套 SQL 语句是由 OData 组件生成的。在调用 ODataQueryOptions.ApplyTo 之后,您的 IQueryable 已被修改,相关的 sql 语句也已被修改。我在我的常规 Web API Controller 中做了一些测试如下,你可以参考它:

要求:

Get http://localhost:58971/api/todoitem?$top=2&$select=Text,Id,Version

在应用 OData 查询选项之前:

应用 OData 查询选项后: