无效/禁用实体框架缓存 [英] Invalidating/Disabling Entity Framework cache

查看:186
本文介绍了无效/禁用实体框架缓存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



直接的问题是



如何完全禁用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 extends BaseManager

  • 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();
}

}
}



尝试




解决方案

如果你想完全忽略EF6的缓存用于数据检索,然后将 AsNoTracking()添加到查询结尾(调用 ToList()或执行执行查询的其他任何东西。



MSDN on AsNoTracking()



轻松注意,这样做既不会检查现有数据的缓存,也不会将数据库调用的结果添加到缓存中。此外,实体框架不会自动检测从数据库检索的实体的更改。如果您想要更改任何实体并将其保存回数据库,则需要在调用 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 extending BaseManager
  • 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

解决方案

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.

MSDN on AsNoTracking()

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆