¿Puedo usar un caché de plan de consulta por sesión?


Pregunta

Tengo una aplicación ASP.NET de varios inquilinos y nuestra base de datos está configurada con eliminaciones de software. Inicialmente, manejamos la restricción de datos directamente en el nivel de consulta, por ejemplo:

var foos = context.Foos.Where(foo => !foo.Deleted && foo.TenantId = currentTenantId).ToList();

Como puede imaginar, esto aumenta todas las consultas en nuestra capa de acceso a datos, y hace que la API sea muy vulnerable si se olvida de agregar las condiciones de filtro correctas. Hemos decidido aplicar el filtrado global al contexto con Z.EntityFramework.Plus.EF6 :

var foos = context.Foos.Where(foo => !foo.Deleted && foo.TenantId = currentTenantId).ToList();

Esto funciona perfectamente para un solo usuario. Sin embargo, cuando cambia de inquilino, tenemos problemas con la expresión de filtro que se está guardando en el caché del plan de consulta . Este es un problema conocido con Entity Framework Plus y, como no parece estar resuelto, necesito encontrar una solución alternativa.

La solución más inmediata que se me ocurre es asociar la vida útil de la caché del plan de consulta a la sesión actual, y cuando el usuario cierra la sesión o cambia de inquilino, la caché se destruye. ¿Es esto posible, y si es así, cómo puedo lograrlo?

Respuesta aceptada

Tuve el mismo problema e intenté trabajar con Z.EntityFramework.Plus.EF6 con los mismos problemas. Descubrí que el equipo de zzzprojects también tiene EntityFramework.DynamicFilters que funciona mucho mejor para este propósito. La consulta que se almacena en caché está parametrizada y el valor se inyecta en tiempo de ejecución utilizando la función de selector que proporciona.

using System.Data.Entity;
using EntityFramework.DynamicFilters;

public class Program
{   
    public class CustomContext : DbContext
    {
        private int _tenantId;

        public int GetTenantId()
        {
            return _tenantId;
        }

        // Call this function to set the tenant once authentication is complete.
        // Alternatively, you could pass tenantId in when constructing CustomContext if you already know it
        // or pass in a function that returns the tenant to the constructor and call it here.
        public void SetTenantId(int tenantId)
        {
            _tenantId = tenantId;
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Filter applies to any model that implements ITenantRestrictedObject
            modelBuilder.Filter(
                "TenantFilter",
                (ITenantRestrictedObject t, int tenantId) => t.TenantId == tenantId,
                (CustomContext ctx) => ctx.GetTenantId(), // Might could replace this with a property accessor... I haven't tried it
                opt => opt.ApplyToChildProperties(false)
            );
        }
    }

    public interface ITenantRestrictedObject
    {
        int TenantId { get; }
    }
}




Licencia bajo: CC-BY-SA
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué