无效/禁用实体框架缓存

时间:2015-01-30 10:03:52

标签: c# linq entity-framework caching

我看到EF缓存有很多问题,但我找不到解决问题的方法。

直接的问题是

如何完全禁用Entity Framework 6缓存?或者,我可以通过编程方式告诉EF忘记缓存,因为数据发生了什么?

背景

首先,我有继承由EF(模型优先定义实体)和普通旧SQL(操纵数据)的奇怪混合构成的应用程序。我所做的是重构应用程序以便:

  • 对实体进行简单查询(如GetAll())使用EF6 LINQ
  • 在SQL中保留复杂数据操作,在需要时使用DbContext.Database.Connection
  • 添加Spring.Web支持以启用DI和交易(尚未)

目前,我已重新组织代码,以便应用程序的主要功能(在大型数据集上运行复杂的SQL查询)像以前一样工作,但随后使用更智能地查找域实体操作大多数实体框架尽可能

最像......

我继承的其中一个页面是一个多重复选框页面,我将向您展示最佳理解。我不会讨论之前的实现者的选择,因为修复我当前的问题和后来的重构代码比阻止破坏功能的开发更便宜。

这就是页面的样子

enter image description here

基本上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
  • 我更改了一项或多项检查并提交
  • 控制器发出查询以更新数据库中的检查
  • 重定向之前(!表示新请求!)重新加载页面,我检查数据库并应用更改
  • 页面重新加载上面发出相同的LINQ选择:显示旧支票

我确信这是一个缓存问题,因为重新加载应用程序可以解决问题。 由于应用程序的主要功能完全基于SQL,因此对查找表的更改将反映到主操作中,并且这是正确的行为。

我知道EF缓存是性能的一个很好的功能,但在我的情况下我只是不想要它,至少在我将整个应用程序迁移到LINQ DML之前(可能是不可能的)。

我如何使用DbContext

当然有些人可能会问"你如何使用你的DbContext?" "你是否正确处理它?"。

  • 我还没有在我的经理类中集成Spring事务
  • 在数据库上执行操作的每个对象都是I<Entity>Manager扩展BaseManager
  • DbContext是一个请求范围的Spring对象。我已经asked about disposing request-scoped objects但我目前实施了一种解决方法,虽然不好,在请求结束时正确处理 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<Models.Example> 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();
        }

    }
}

我尝试了什么

2 个答案:

答案 0 :(得分:33)

如果要完全忽略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缓存的其他选项感兴趣,我写了blog post about EF6 Cache Busting

答案 1 :(得分:4)

我也有这个麻烦,但我可以解决它。

我使用Repository Pattern并使用.Net Core的默认DI。 我一直在使用AddSingleton(...),但使用DbContext是错误的。

所以,我已经改为AddScoped,就像我从文档中读到的那样:每个请求都会创建一次Scoped终身服务。

它解决了我的问题。

You must read this section from ms docs: Service Lifetimes and Registration Options