Posso utilizzare una cache del piano di query separata per sessione?


Domanda

Ho un'applicazione ASP.NET multi-tenant e il nostro database è impostato con eliminazioni software. Inizialmente, abbiamo gestito la restrizione dei dati direttamente a livello di query, ad esempio:

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

Come puoi immaginare, questo mette a dura prova tutte le query nel nostro livello di accesso ai dati e rende l'API molto vulnerabile se si dimentica di aggiungere le condizioni di filtro corrette. Abbiamo deciso di applicare il filtro globale al contesto con Z.EntityFramework.Plus.EF6 :

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

Questo funziona perfettamente per un singolo utente. Tuttavia, quando si cambia titolare, si verificano problemi con l'espressione filtro salvata nella cache del piano di query . Questo è un problema noto con Entity Framework Plus, e poiché non sembra essere risolto, ho bisogno di trovare una soluzione alternativa.

La soluzione più immediata che riesco a pensare è quella di associare la durata della cache del piano di query alla sessione corrente, e quando l'utente si disconnette o cambia il tenant, la cache viene distrutta. È possibile, e se sì, come posso ottenerlo?

Risposta accettata

Ho avuto questo stesso identico problema e ho provato a lavorare con Z.EntityFramework.Plus.EF6 con gli stessi problemi. Ho scoperto che il team di zzzprojects ha anche EntityFramework.DynamicFilters che funziona molto meglio per questo scopo. La query che viene memorizzata nella cache viene parametrizzata e il valore viene iniettato in fase di esecuzione utilizzando la funzione di selezione fornita dall'utente.

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




Autorizzato sotto: CC-BY-SA
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché