Invalider / désactiver le cache Entity Framework

Je vois qu’il y a beaucoup de questions sur le cache EF, mais je n’ai pas encore trouvé de solution à mon problème.

La question directe est

Comment désactiver complètement le cache Entity Framework 6? Ou, puis-je demander par programme à EF d’oublier le cache, car quelque chose est arrivé aux données?

Contexte

Premièrement, j’ai hérité d’ une application faite d’un mélange étrange de EF (modèle-premier pour définir des entités) et plaine vieux SQL (pour manipuler des données). Ce que j’ai fait, c’est de refactoriser l’application pour:

  • Faire des requêtes simples (comme GetAll() pour une entité) utiliser EF6 LINQ
  • Laisser des manipulations complexes de données dans SQL, en utilisant DbContext.Database.Connection cas de besoin
  • Ajouter Spring.Web support Spring.Web pour activer DI et les transactions (pas encore)

À ce stade, j’ai réorganisé le code de sorte que la fonction principale de l’application (exécuter des requêtes SQL complexes sur des jeux de données volumineux) fonctionne comme auparavant, mais la manipulation des entités de domaine de recherche est ensuite effectuée plus intelligemment en utilisant le plus possible Entity Framework.

Comme la plupart des ….

Une des pages dont j’ai hérité est une page à coches multiples que je vais vous montrer pour une meilleure compréhension. Je ne discuterai pas du choix de l’ancien implémenteur, car il est moins coûteux de résoudre mon problème actuel et de modifier plus tard le code de refactorisation plutôt que de bloquer le développement d’une fonctionnalité défectueuse.

Voici à quoi ressemble la page

entrez la description de l'image ici

Fondamentalement, la méthode du Controller est la suivante

  [HttpPost] public ActionResult Index(ssortingng[] codice, ssortingng[] flagpf, ssortingng[] flagpg, ssortingng[] flagammbce, ssortingng[] flagammdiv, ssortingng[] flagammest, ssortingng[] flagintab, ssortingng[] flagfinanz, ssortingng[] flagita, ssortingng[] flagest, ssortingng pNew){ Sottogruppo2015Manager.ActivateFlagFor("pf", flagpf); Sottogruppo2015Manager.ActivateFlagFor("pg", flagpg); Sottogruppo2015Manager.ActivateFlagFor("ammbce", flagammbce); Sottogruppo2015Manager.ActivateFlagFor("ammdiv", flagammdiv); Sottogruppo2015Manager.ActivateFlagFor("ammest", flagammest); Sottogruppo2015Manager.ActivateFlagFor("intab", flagintab); Sottogruppo2015Manager.ActivateFlagFor("finanz", flagfinanz); Sottogruppo2015Manager.ActivateFlagFor("ita", flagita); Sottogruppo2015Manager.ActivateFlagFor("est", flagest); return RedirectToAction("Index", new { pNew }); } 

Chaque paramètre ssortingng[] est une colonne de la table. La méthode ActivateFlagFor exécute deux requêtes en séquence

 UPDATE table SET --param1-- = 0; UPDATE table SET --param1-- = 1 where id in (--param2--) 

Quand la cache entre en jeu

Ce qui suit est le comportement:

  • Je commence par charger la page générant une sélection LINQ: les vérifications correspondent à celles et aux zéros des colonnes
  • Je change un ou plusieurs chèques et soumets
  • Le contrôleur émet les requêtes pour mettre à jour les vérifications dans la firebase database.
  • Avant de redirect (! Signifie nouvelle requête!) Pour recharger la page, je vérifie le DB et les modifications sont appliquées
  • Les pages rechargées émettant le même LINQ sélectionnent ci-dessus: les anciens contrôles sont affichés

Je suis sûr qu’il s’agit d’un problème de mise en cache, car le rechargement de l’application corrige le problème. Étant donné que la fonctionnalité principale de l’application est entièrement basée sur SQL, les modifications apscopes aux tables de recherche sont reflétées dans l’opération principale et constituent le comportement correct.

Je comprends que la mise en cache EF est une fonctionnalité intéressante pour les performances, mais dans mon cas, je ne la souhaite tout simplement pas, du moins tant que je n’ai pas migré l’ensemble de l’application vers LINQ DML (probablement impossible).

Comment j’utilise le DbContext

Bien sûr, certains d’entre vous peuvent demander “comment utilisez-vous votre DbContext?” “en disposez-vous correctement?”

  • Je n’ai pas encore intégré les transactions Spring dans mes classes de responsable
  • Chaque object qui effectue des actions sur la firebase database est un IManager étendant BaseManager
  • DbContext est un object Spring DbContext une requête. J’ai déjà posé une question sur la disposition des objects liés à la requête, mais j’ai actuellement implémenté une solution de contournement qui, bien que mauvaise, supprime correctement le DbContext à la fin de la demande.

Exemple de code

 public class ExampleManagerImpl : BaseManager, IExampleManager { public void ActivateFlagFor(ssortingng aFlag, ssortingng[] aList) { ssortingng sql = "UPDATE table SET flag" + aFlag + " = 0"; RunStatementV1(sql); if (aList != null && aList.Any()) { sql = "UPDATE table SET flag" + aFlag + " = 1 WHERE id in (" + aList.ToCsvApex() + ")"; RunStatementV1(sql); } } public IList GetAll() { return DataContext.example.ToList(); //I don't dispose of the DataContext willingly } } 

et

 public abstract class BaseManager { public DbContext DataContext { get; set; } //Autowired protected void RunStatementV1(ssortingng aSqlStatement) { IDbConnection connection = DataContext.Database.Connection; if (connection.State == ConnectionState.Closed || connection.State == ConnectionState.Broken) connection.Open(); //Needed because sometimes I found the connection closed, even if I don't dispose of it using (IDbCommand command = connection.CreateCommand()) { command.CommandText = aSqlStatement; command.ExecuteNonQuery(); } } } 

Ce que j’ai essayé

  • Comment arrêter la mise en cache de l’entité : la désactivation du chargement différé n’a pas fonctionné
  • https://stackoverflow.com/a/22818783/471213 : Detach semble résoudre le problème, mais il faudrait que je l’applique à une douzaine d’entités pour pouvoir revenir un jour plus tard.

Si vous souhaitez ignorer complètement le cache de EF6 pour la récupération de données, ajoutez AsNoTracking() à la fin de votre requête (avant d’appeler ToList() ou de faire autre chose qui l’exécuterait.

MSDN sur AsNoTracking()

Veuillez noter que cela ne vérifiera pas le cache pour les données existantes, ni n’appenda les résultats de l’appel de la firebase database au cache. En outre, Entity Framework ne détectera pas automatiquement les modifications apscopes aux entités extraites de la firebase database. Si vous souhaitez modifier des entités et les enregistrer dans la firebase database, vous devez attacher les entités modifiées avant d’appeler SaveChanges() .

Votre méthode est actuellement:

 public IList GetAll() { return DataContext.example.ToList(); } 

Cela changerait à:

 public IList GetAll() { return DataContext.example.AsNoTracking().ToList(); } 

Si vous êtes intéressé par d’autres options pour traiter le cache d’EF, j’ai écrit un article de blog sur EF6 Cache Busting .

J’ai eu ce problème aussi, mais je pouvais le réparer.

J’utilise le modèle de référentiel et j’utilise le DI par défaut de .Net Core. J’utilise AddSingleton (…), mais c’est mal d’utiliser avec DbContext.

J’ai donc changé pour AddScoped, comme je l’ai lu dans docs: Les services de durée de vie Scoped sont créés une fois par demande.

Cela a résolu mon problème.

Vous devez lire cette section à partir de ms docs: Durée de vie du service et options d’enregistrement