c# - ¿Por qué Entity Framework genera los siguientes controladores de tabla anidados de SQL para Azure Mobile Services?
entity-framework asp.net-web-api2 (2)
Estoy tratando de llegar al fondo de un problema de Framework de la entidad cuando lo uso con un TableController
He creado la siguiente configuración.
El ejemplo básico de TodoItem provisto con una nueva API web móvil que aprovecha EntityFramework, TableController y el EntityDomainManager predeterminado
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; }
Un controlador web 2 de vainilla.
public class TodoItemsWebController : ApiController { private MobileServiceContext db = new MobileServiceContext(); public TodoItemsWebController() { db.Database.Log += LogToDebug; } public IQueryable<TodoItem> GetTodoItems() { return db.TodoItems; }
tablecontroller
código del tablecontroller
con un peine de dientes finos y me tablecontroller
al método Query
, que es simplemente un proxy de la llamada a través del DomainManager
para agregar la modificación Where(_ => !_.IsDeleted)
Sin embargo, las dos consultas producen SQL muy diferente.
Para el controlador de API web normal, obtiene el siguiente 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]
Pero para TableController, obtienes la siguiente parte de SQL que tiene un * Magic * Guid en el medio, y da como resultado una declaración de SQL anidado. El rendimiento de esto se completa con la basura cuando comienza a tratar con cualquiera de las consultas ODATAv3 como $ top, $ skip, $ filter y $ 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
Puedes ver los resultados de ambas consultas aquí. https://pastebin.com/tSACq6eg
Así que mis preguntas son:
¿Por qué el
TableController
genera el SQL de esta manera?¿Qué es el * magic * guid en medio de la consulta? (se mantendrá igual hasta que detenga y reinicie la aplicación, así que no sé si es la sesión, el cliente o el contexto de DB específico)
¿Dónde exactamente en la tubería está el TableController haciendo estas modificaciones al
IQueryable
? Supongo que se hace a través de algún paso de middleware o un atributo ejecutado más adelante en la solicitud después de llamar al métodoQuery()
, pero no puedo encontrarlo por mi vida.
De acuerdo con su descripción, investigué un poco y descubrí que Azure Mobile Server SDK usa la siguiente línea de código en TableControllerConfigProvider.cs para agregar filtros relacionados con consultas adicionales para las mismas acciones con el QueryableAttribute para habilitar una acción del controlador para admitir los parámetros de consulta OData.
controllerSettings.Services.Add(typeof(IFilterProvider), new TableFilterProvider());
Nota: los filtros adicionales se ejecutarán después de que su acción se haya ejecutado y devuelva el IQueryable
.
Podría verificar EnableQueryAttribute.cs y descubrió que OnActionExecuted
llamaría ExecuteQuery
método ExecuteQuery
y finalmente llamaría a ODataQueryOptions.ApplyTo para aplicar las opciones de consulta OData ($ filter, $ orderby, $ top, $ skip, y $ inlinecount, etc.) al IQueryable dado .
Según tengo entendido, la instrucción SQL anidada es generada por el componente OData. Después de invocar ODataQueryOptions.ApplyTo
, su IQueryable se ha modificado y la instrucción SQL relacionada también se ha modificado. Hice algunas pruebas en mi controlador de API web habitual de la siguiente manera, puede consultar:
Solicitud:
Get http://localhost:58971/api/todoitem?$top=2&$select=Text,Id,Version
Antes de aplicar las opciones de consulta OData:
Después de aplicar las opciones de consulta OData:
Una de las tablas se está sincronizando entre el backend y el cliente porque si está obteniendo el segundo sql.