c# entity-framework asp.net-web-api2 odata azure-mobile-services

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.

  1. 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; }

  2. 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étodo Query() , 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: