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.
Comment désactiver complètement le cache Entity Framework 6? Ou, puis-je dire par programme à EF d'oublier le cache car quelque chose est arrivé aux données?
Tout d'abord, j'ai hérité d'une application faite d'un étrange mélange d'EF (modèle d'abord pour définir les entités) et de SQL ancien (pour manipuler les données). Ce que j'ai fait, c'est de refactoriser l'application afin de:
GetAll()
pour une entité) utilisez EF6 LINQDbContext.Database.Connection
si nécessaireSpring.Web
support pour activer DI et les transactions (pas encore)À l'heure actuelle, j'ai réorganisé le code afin que la fonction principale de l'application (exécution de requêtes SQL complexes sur d'énormes ensembles de données) fonctionne comme auparavant, mais la recherche d'entités de domaine se fait plus intelligemment en utilisant comme la plupart Entity Framework possible
L'une des pages dont j'ai hérité est une page à cases à cocher multiples que je vais vous montrer pour une meilleure compréhension. Je ne discuterai pas du choix de l'implémentateur précédent, car il est moins coûteux de résoudre mon problème actuel et le code de refactorisation ultérieur que de bloquer le développement d'une fonctionnalité cassée.
Voici à quoi ressemble la page
Fondamentalement, la méthode Controller
est la suivante
[HttpPost]
public ActionResult Index(string[] codice, string[] flagpf, string[] flagpg, string[] flagammbce, string[] flagammdiv, string[] flagammest,
string[] flagintab, string[] flagfinanz, string[] flagita, string[] flagest, string 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 string[]
paramètre est une colonne du tableau. 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--)
Voici le comportement:
Je suis sûr qu'il s'agit d'un problème de mise en cache, car le rechargement de l'application résout le problème. Étant donné que la principale caractéristique de l'application est entièrement basée sur SQL, les modifications apportées aux tables de recherche se reflètent dans le fonctionnement principal et c'est le comportement correct.
Je comprends que la mise en cache EF est une excellente fonctionnalité pour les performances, mais dans mon cas, je ne le veux tout simplement pas, du moins jusqu'à ce que je migre toute l'application vers LINQ DML (probablement impossible).
DbContext
Bien sûr, certains d'entre vous peuvent se demander "comment utilisez-vous votre DbContext?" "vous en débarrassez-vous correctement?".
I<Entity>Manager
extension BaseManager
DbContext
est un objet Spring à portée de requête. J'ai déjà interrogé sur la suppression des objets à portée de 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.public class ExampleManagerImpl : BaseManager, IExampleManager
{
public void ActivateFlagFor(string aFlag, string[] aList)
{
string 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<Models.Example> 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(string 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();
}
}
}
Detach
semble résoudre le problème mais je devrais l'appliquer à une douzaine d'entités, afin de revenir un jour A l'avenirSi vous souhaitez ignorer complètement le cache d'EF6 pour la récupération des données, ajoutez AsNoTracking()
à la fin de votre requête (avant d'appeler ToList()
ou de faire quoi que ce soit qui exécuterait la requête.
Veuillez noter que cela ne vérifiera pas le cache pour les données existantes, ni ajoutera les résultats de l'appel de base de données au cache. En outre, Entity Framework ne détectera pas automatiquement les modifications apportées aux entités que vous récupérez de la base de données. Si vous souhaitez modifier des entités et les sauvegarder dans la base de données, vous devrez attacher les entités modifiées avant d'appeler SaveChanges()
.
Votre méthode est actuellement:
public IList<Models.Example> GetAll()
{
return DataContext.example.ToList();
}
Il se changerait en:
public IList<Models.Example> GetAll()
{
return DataContext.example.AsNoTracking().ToList();
}
Si vous êtes intéressé par d'autres options pour gérer le cache d'EF, j'ai écrit un blog sur EF6 Cache Busting .
J'ai aussi eu ce problème, mais je pouvais le réparer.
J'utilise le modèle de référentiel et j'utilise DI par défaut de .Net Core. J'utilise AddSingleton (...), mais il est faux de l'utiliser avec DbContext.
J'ai donc changé pour AddScoped, comme je l'ai lu dans les documents: Les services de durée de vie sont créés une fois par demande.
Cela a résolu mon problème.
Vous devez lire cette section de ms docs: Service Lifetimes and Registration Options