无效/禁用entity framework缓存
我看到EF缓存有很多问题,但我找不到解决问题的方法。
直截了当的问题是
如何完全禁用Entity Framework 6缓存? 或者,我可以通过编程方式告诉EF忘记缓存,因为数据发生了什么?
背景
首先,我inheritance了一个由EF(模型优先定义实体)和普通旧SQL(操纵数据)的奇怪混合构成的应用程序。 我做的是重构应用程序,以便:
- 简单查询(如实体的
GetAll()
)使用EF6 LINQ - 在SQL中保留复杂的数据操作,在需要时使用
DbContext.Database.Connection
- 添加
Spring.Web
支持以启用DI和事务(尚未)
在当前,我重新组织了代码,以便应用程序的主要function(在大型数据集上运行复杂的SQL查询)像以前一样工作,但随后使用尽可能多的entity framework更智能地完成查找域实体操作
大多数……
我inheritance的其中一个页面是一个多重复选框页面,我将向您展示以便最好地理解。 我不会讨论之前的实现者的选择,因为修复我当前的问题和后来的重构代码比阻止破坏function的开发更便宜。
这就是页面的样子
基本上Controller
方法如下
[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 }); }
每个string[]
参数都是表中的一列。 ActivateFlagFor
方法按顺序运行两个查询
UPDATE table SET --param1-- = 0; UPDATE table SET --param1-- = 1 where id in (--param2--)
当缓存启动时
以下是行为:
- 我首先加载发出LINQ select的页面:检查匹配列中的1和0
- 我更改了一个或多个支票并提交
- 控制器发出查询以更新DB中的检查
- 在重定向 (!表示新请求!)重新加载页面之前,我检查数据库并应用更改
- 页面重新加载发出上面相同的LINQ选择: 显示旧的检查
我确信这是一个缓存问题,因为重新加载应用程序可以解决问题。 由于应用程序的主要function完全基于SQL,因此对查找表的更改将反映到主操作中,这是正确的行为。
我知道EF缓存是性能的一个很好的function,但在我的情况下我只是不想要它,至少在我将整个应用程序迁移到LINQ DML之前(可能是不可能的)。
我如何使用DbContext
当然有些人可能会问“你如何使用你的DbContext?” “你能正确处理它吗?”
- 我尚未在Manager类中集成Spring事务
- 在数据库上执行操作的每个对象都是扩展
BaseManager
的IManager
-
DbContext
是一个请求范围的Spring对象。 我已经问过处理请求范围的对象,但我目前实现了一种解决方法,虽然不好,但在请求结束时正确处理了DbContext。
示例代码
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 GetAll() { return DataContext.example.ToList(); //I don't dispose of the DataContext willingly } }
和
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(); } } }
我尝试了什么
- 如何停止entity framework缓存 :禁用延迟加载不起作用
- https://stackoverflow.com/a/22818783/471213 :
Detach
似乎可以解决问题,但我需要将它应用于十几个实体,以便将来某天恢复
如果要完全忽略AsNoTracking()
数据检索缓存,则在查询结束时添加AsNoTracking()
(在调用ToList()
之前或执行任何其他可执行查询的操作。
AsNoTracking()
MSDN AsNoTracking()
请注意,这样做既不会检查现有数据的缓存,也不会将数据库调用的结果添加到缓存中。 此外,entity framework不会自动检测您从数据库中检索的实体的更改。 如果您确实想要更改任何实体并将它们保存回数据库,则需要在调用SaveChanges()
之前附加更改的实体。
你的方法目前是:
public IList GetAll() { return DataContext.example.ToList(); }
它将改为:
public IList GetAll() { return DataContext.example.AsNoTracking().ToList(); }
如果您对处理EF缓存的其他选项感兴趣,我写了一篇关于EF6 Cache Busting的博客文章 。
我也有这个麻烦,但我可以解决它。
我正在使用Repository Pattern并使用.Net Core的默认DI。 我一直在使用AddSingleton(…),但使用DbContext是错误的。
所以,我已经改为AddScoped,就像我从文档中读到的那样: 每个请求创建一次生命周期服务。
它解决了我的问题。
您必须从ms文档中阅读本节:服务生命周期和注册选项