无效/禁用实体框架缓存 [英] Invalidating/Disabling Entity Framework cache
问题描述
直接的问题是
如何完全禁用Entity Framework 6缓存?或者,我可以通过编程方式告诉EF忘记数据缓存吗?
背景
,我有继承应用程序,由一个奇怪的混合物(首先定义实体的模型)和纯旧的SQL(操纵数据)组成。我做的是重构应用程序,以便:
- 进行简单的查询(如
GetAll()
用于实体)使用EF6 LINQ - 使用
DbContext.Database.Connection
在SQL中保留复杂数据操作需要 - 添加
Spring.Web
支持启用DI和交易(尚未)
目前,我重组了代码,使得应用程序的主要功能(在巨大的数据集上运行复杂的SQL查询)的工作原理与之前一样,但是查找域实体使用大多数实体框架来更好地使用操作
大多数....
我继承的一个页面是一个多复选框页面,我将向您展示最佳了解。我不会讨论以前的实现者的选择,因为修复我当前的问题和后来的重构代码比阻止开发的功能更便宜,这是一个破坏的功能。
这是页面看起来像
基本上, Controller
方法是以下
[HttpPost]
public ActionResult索引(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);
返回RedirectToAction(Index,new {pNew});
}
每个 string []
parameter是表中的一列。 ActivateFlagFor
方法按顺序运行两个查询
UPDATE表SET - param1-- = 0;
更新表SET --param1-- = 1其中id((--param2--))
缓存踢入
以下是行为:
- 我首先加载发布LINQ选择的页面:检查匹配列中的零和
- 我更改一个或多个支票并提交
- 控制器发出查询以更新DB中的检查
- 在重定向之前(!表示新请求!)重新加载页面,我查看数据库,更改是应用
- 页面重新加载发出上述相同的LINQ选项:显示旧支票
我确定这是一个缓存问题,因为重新加载应用程序可以解决问题。
由于应用程序的主要功能完全是基于SQL,所以查找表的更改将反映到主要操作中,这是正确的行为。
我明白EF缓存是性能的一个很好的功能,但在我的情况下,我只是不想要它,至少在我将整个应用程序迁移到LINQ DML(可能不可能)之前。
我如何使用 DbContext
当然有些人可能会问你如何使用DbContext? 您是否正确处理?
- 我尚未在我的经理课程中集成Spring交易
- 在数据库上执行操作的每个对象都是一个
I< Entity> Manager
extendsBaseManager
-
DbContext
是一个请求范围的Spring对象。我已经询问有关处理请求范围的对象,但是我目前已经实施了一个解决方案,虽然不好,
示例代码
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表SET标志+ aFlag += 1 WHERE id in (+ aList.ToCsvApex()+);
RunStatementV1(sql);
}
}
public IList< Models.Example> GetAll()
{
return DataContext.example.ToList(); //我不会自动处理DataContext
}
}
和
public abstract class BaseManager {
public DbContext DataContext {get;组; } // Autowired
protected void RunStatementV1(string aSqlStatement)
{
IDbConnection connection = DataContext.Database.Connection;
if(connection.State == ConnectionState.Closed || connection.State == ConnectionState.Broken)connection.Open(); //需要,因为有时我发现连接关闭,即使我不处理它
使用(IDbCommand command = connection.CreateCommand())
{
command.CommandText = aSqlStatement;
command.ExecuteNonQuery();
}
}
}
尝试
- 如何停止实体框架缓存:禁用延迟加载不起作用
- https://stackoverflow.com/a/22818783/471213 :
分离
似乎解决了这个问题,但是我需要将它应用到十几个实体,为了在将来有一天恢复
如果你想完全忽略EF6的缓存用于数据检索,然后将 AsNoTracking()
添加到查询结尾(调用 ToList()
或执行执行查询的其他任何东西。
轻松注意,这样做既不会检查现有数据的缓存,也不会将数据库调用的结果添加到缓存中。此外,实体框架不会自动检测从数据库检索的实体的更改。如果您想要更改任何实体并将其保存回数据库,则需要在调用 SaveChanges()
之前附加更改的实体。
您的方法目前是:
public IList< Models.Example> GetAll()
{
return DataContext.example.ToList();
}
它会更改为:
public IList< Models.Example> GetAll()
{
return DataContext.example.AsNoTracking()。ToList();
}
如果您对处理EF缓存的其他选项感兴趣,撰写了关于EF6 Cache Busting的博客文章。
I see there are plenties of question on EF cache, but I have found no solution yet to my problem.
The straight question is
How do I completely disable Entity Framework 6 cache? Or, can I programmatically tell EF to forget about the cache because something happened to data?
Background
First, I have inherited an application made of a strange mixture of EF (model-first to define entities) and plain old SQL (to manipulate data). What I did was to refactor the application in order to:
- Make simple queries (like
GetAll()
for an entity) use EF6 LINQ - Leave complex data manipulation in SQL, using
DbContext.Database.Connection
when needed - Add
Spring.Web
support to enable DI and transactions (not yet)
At the current point, I have reorganized the code so that the main function of the application (running complex SQL queries on huge datasets) works as it did before, but then lookup domain entity manipulation is done smarter using as most Entity Framework as possible
As most....
One of the pages I inherited is a multi-checkbox page I'm going to show you for best understanding. I won't discuss the previous implementor's choice, because it's cheaper to fix my current problem and later refactor code than blocking development for a broken feature.
This is how the page looks like
Basically the Controller
method is the following
[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 });
}
Each string[]
parameter is a column in the table. The ActivateFlagFor
method runs two queries in sequence
UPDATE table SET --param1-- = 0;
UPDATE table SET --param1-- = 1 where id in (--param2--)
When the cache kicks in
The following is the behaviour:
- I first load the page issuing a LINQ select: checks match the ones and zeroes in columns
- I change one or more checks and submit
- The controller issues the queries to update checks in the DB
- Before redirecting (!means new request!) to reload the page, I check the DB and the changes are applied
- The page reloads issuing the same LINQ select above: old checks are displayed
I am sure that this is a caching problem, because reloading the application fixes the problem. Since the main feature of the application is totally SQL based, changes to lookup tables are reflected into main operation and that's the correct behaviour.
I understand that EF caching is a great feature for performance, but in my case I just don't want it, at least until I migrate the whole application to LINQ DML (probably impossible).
How I use the DbContext
Of course some of you may ask "how do you use your DbContext?" "do you dispose of it correctly?".
- I haven't yet integrated Spring transactions in my Manager classes
- Each object that performs actions on the database is a
I<Entity>Manager
extendingBaseManager
DbContext
is a request-scoped Spring object. I already asked about disposing request-scoped objects but I currently implemented a workaround that, while bad, correctly disposes of the DbContext at the end of the request.
Example code
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
}
}
and
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();
}
}
}
What I tried
- How to stop entity framework caching : disabling lazy loading didn't work
- https://stackoverflow.com/a/22818783/471213:
Detach
seems to solve the problem but I'd need to apply it to dozen of entities, in order to revert someday in the future
If you want to completely ignore EF6's cache for data retrieval, then add AsNoTracking()
to the end of your query (before calling ToList()
or doing anything else that would execute the query.
Please note that doing so will neither check the cache for existing data, nor will it add the results of the database call to the cache. Additionally, Entity Framework will not automatically detect changes to entities that you retrieve from the database. If you do want to change any entities and save them back to the database, you'll need to attach the changed entities before calling SaveChanges()
.
Your method is currently:
public IList<Models.Example> GetAll()
{
return DataContext.example.ToList();
}
It would change to:
public IList<Models.Example> GetAll()
{
return DataContext.example.AsNoTracking().ToList();
}
If you are interested in other options for dealing with EF's cache, I've written a blog post about EF6 Cache Busting.
这篇关于无效/禁用实体框架缓存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!