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

Eoi*_*ell 16 c# entity-framework odata azure-mobile-services asp.net-web-api2

我正在尝试使用它时将实体框架问题置于底层 TableController

我创建了以下设置.

  1. 基本的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;
        }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 一个vanilla 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;
        }
    
    Run Code Online (Sandbox Code Playgroud)

tablecontroller用细齿梳子完成了代码,深入研究了Query方法,这只是通过DomainManager添加Where(_ => !_.IsDeleted)修改来代替调用.IQueryable

然而,这两个查询产生了非常不同的SQL.

对于常规Web API Controller,您将获得以下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]
Run Code Online (Sandbox Code Playgroud)

但对于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
Run Code Online (Sandbox Code Playgroud)

您可以在此处查看两个查询的结果.https://pastebin.com/tSACq6eg

所以我的问题是:

  • 为什么TableController 以这种方式生成SQL?

  • 查询中间的*magic* guid是什么?(它将保持不变,直到我停止并重新启动应用程序,因此我不知道它是否是会话,客户端或数据库上下文特定)

  • 究竟在管道中的TableController进行这些修改IQueryable?我假设它是通过一些中间件步骤完成的,或者在Query()调用该方法之后在请求中执行后执行的属性,但我不能在我的生活中找到它.

Bru*_*hen 5

根据您的描述,我做了一些研究,发现Azure Mobile Server SDK使用TableControllerConfigProvider.cs下的以下代码行为QueryableAttribute添加与相同操作的其他查询相关过滤器,以使控制器操作能够支持OData查询参数.

controllerSettings.Services.Add(typeof(IFilterProvider), new TableFilterProvider());
Run Code Online (Sandbox Code Playgroud)

注意:执行操作后将执行其他过滤器并返回IQueryable.

您可以检查EnableQueryAttribute.cs并发现OnActionExecuted将调用该ExecuteQuery方法并最终调用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
Run Code Online (Sandbox Code Playgroud)

在应用OData查询选项之前:

在此输入图像描述

应用OData查询选项后:

在此输入图像描述