为什么 Entity Framework 为 Azure 移动服务 Table 控制器生成以下嵌套 SQL
Why is Entity Framework generating the following nested SQL for Azure Mobile Services Table Controllers
当我将它与 TableController
一起使用时,我正试图弄清 entity Framework 问题的根源
我创建了以下设置。
基本的 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;
}
香草网 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
所以我的问题是:
为什么TableController
会这样生成SQL?
查询中间的*magic*guid是什么? (它会保持不变,直到我停止并重新启动应用程序,所以我不知道它是特定于会话、客户端还是数据库上下文)
TableController 在管道中对 IQueryable
进行这些修改的确切位置?我假设它是在调用 Query()
方法后通过一些中间件步骤或稍后在请求中执行的属性完成的,但我一生都找不到它。
您的其中一张表正在后端和客户端之间同步,因为如果那样您将获得第二个 sql。
根据您的描述,我做了一些研究,发现 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 查询选项后:
当我将它与 TableController
我创建了以下设置。
基本的 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; }
香草网 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
所以我的问题是:
为什么
TableController
会这样生成SQL?查询中间的*magic*guid是什么? (它会保持不变,直到我停止并重新启动应用程序,所以我不知道它是特定于会话、客户端还是数据库上下文)
TableController 在管道中对
IQueryable
进行这些修改的确切位置?我假设它是在调用Query()
方法后通过一些中间件步骤或稍后在请求中执行的属性完成的,但我一生都找不到它。
您的其中一张表正在后端和客户端之间同步,因为如果那样您将获得第二个 sql。
根据您的描述,我做了一些研究,发现 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 查询选项后: