Kann ich einen separaten Abfrageplan-Cache pro Sitzung verwenden?


Frage

Ich habe eine mandantenfähige ASP.NET-Anwendung, und unsere Datenbank ist mit Soft-Löschungen eingerichtet. Zunächst haben wir die Einschränkung der Daten direkt auf der Abfrageebene behandelt, zB:

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

Wie Sie sich vorstellen können, bläht dies alle Abfragen in unserer Datenzugriffsebene auf und macht die API sehr anfällig, wenn vergessen wird, die richtigen Filterbedingungen hinzuzufügen. Wir haben uns entschieden, den globalen Filter auf den Kontext mit Z.EntityFramework.Plus.EF6 :

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

Dies funktioniert perfekt für einen einzelnen Benutzer. Wenn Sie jedoch den Mandanten wechseln, treten Probleme mit dem Filterausdruck auf, der im Abfrageplancache gespeichert wird. Dies ist ein bekanntes Problem mit Entity Framework Plus, und da es anscheinend nicht gelöst ist, muss ich eine Problemumgehung finden.

Die unmittelbarste Lösung, die ich mir vorstellen kann, besteht darin, die Lebensdauer des Abfrageplancaches der aktuellen Sitzung zuzuordnen, und wenn der Benutzer sich abmeldet oder den Mandanten wechselt, wird der Cache zerstört. Ist das möglich und wenn ja, wie kann ich das erreichen?

Akzeptierte Antwort

Ich hatte genau dieses Problem und versuchte mit Z.EntityFramework.Plus.EF6 mit den gleichen Problemen zu arbeiten. Ich habe festgestellt, dass das zzzprojects-Team auch EntityFramework.DynamicFilters hat, das für diesen Zweck viel besser funktioniert. Die zwischengespeicherte Abfrage wird parametrisiert und der Wert wird zur Laufzeit mit der von Ihnen bereitgestellten Auswahlfunktion injiziert.

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




Lizenziert unter: CC-BY-SA
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum