如何刷新Entity Framework Core DBContext?

MiD*_*Daa 16 c# entity-framework-core .net-core

当我的表由另一方更新时,dotnet核心中的db上下文仍然返回旧值,如何强制Db上下文刷新?

我已经完成了研究,但我只发现人们使用ReloadEF核心无法使用的方法来强制刷新上下文.

其他一些解决方案建议在使用后配置上下文,但是我得到错误说DB上下文是通过依赖注入创建的,我不应该搞砸它.

bin*_*nki 12

依赖注入和DbContext

您提到当您尝试重新创建时DbContext,您会收到有关由依赖注入(DI)系统管理的上下文的错误.使用依赖注入系统进行对象创建有两种不同的样式.DI可以创建一个全局单例实例,该实例在所有使用者之间作为服务共享,也可以为每个作用域/工作单元创建一个实例(例如,在Web服务器中按请求).

如果您的DI系统配置为创建单个全局共享实例DbContext,那么您将遇到与长期相关的各种问题DbContext.DbContext按照设计,永远不会自动从缓存中删除对象,因为它的设计并不长久.因此,长寿DbContext会浪费内存.此外,您的代码永远不会看到加载到其缓存中的项目的更改,而无需手动重新加载它加载的每个实体.因此,最佳做法是DbContext每单位工作使用一个.您的DI系统可以通过配置为应用程序在范围内处理的每个请求提供新的实例来帮助您解决此问题.例如,ASP.NET Core的依赖注入系统支持按请求确定范围.

刷新单个实体

获取新数据的最简单方法是创建一个新数据DbConcurrencyException.但是,在您的工作单元内,或在DI系统提供的作用域粒度限制范围内,您可能会触发一个外部进程,该进程应该直接在数据库中修改您的实体.在退出DI范围或完成工作单元之前,您可能需要查看更改.在这种情况下,您可以通过分离数据对象的实例来强制重新加载.

要做到这一点,首先得到DbContext你的对象.这是一个允许您操作DbContext该对象的内部缓存的对象.然后,您可以通过设置EntityEntry<>DbContext属性来标记此条目.我相信这EntitytState.Detached会将条目留在缓存中,但会导致在将来实际加载条目时删除并替换它.重要的是它会导致将来的加载将新加载的实体实例返回到您的代码中.例如:

var thing = context.Things.Find(id);
if (thing.ShouldBeSentToService) {
    TriggerExternalServiceAndWait(id);

    // Detach the object to remove it from context’s cache.
    context.Entities(thing).State = EntityState.Detached;

    // Then load it. We will get a new object with data
    // freshly loaded from the database.
    thing = context.Things.Find(id);
}
UseSomeOtherData(thing.DataWhichWasUpdated);
Run Code Online (Sandbox Code Playgroud)

  • 另外值得注意的是,当您注册 DbContext 时,它还会注册 `DbContextOptions&lt;T&gt;`,其中 `T` 是您的上下文类型。所以你实际上可以注入它(例如`DbContextOptions&lt;RRStoreContext&gt; rrContextOptions`)并根据需要创建一个全新的上下文(`using (var db = new RRContext(rrContextOptions))`。你必须做出判断与其他答案相比,这是否最适合,但有时这是最简单的方法,因为它们不公开公开重置功能(它确实存在并由 DbContext 池功能使用)。 (4认同)
  • 很好的答案,我使用了一个简短的实时上下文来解决该项目中的问题。谢谢,希望这个答案能帮助其他人。 (2认同)

Jam*_*ake 9

随着 .NET 5.0 和 Entity Framework Core 5.0 的发布,推荐的模式是使用DBContext 工厂。在 Statup.cs 中我改变了:

services.AddDbContext<MyDbContext>...
Run Code Online (Sandbox Code Playgroud)

services.AddDbContextFactory<MyDbContext>...
Run Code Online (Sandbox Code Playgroud)

我的所有存储库类都使用相同的基类,这里我在构造函数中创建上下文:

protected BaseRepository(IDbContextFactory<MyDbContext> contextFactory)
{
    _context = contextFactory.CreateDbContext();
}
Run Code Online (Sandbox Code Playgroud)

我使用一个非常简单的存储库工厂来确保每次需要时都能获得存储库和 dbcontext 的新实例:

using System;
using Data.Models.Entities;
using Microsoft.Extensions.DependencyInjection;

namespace Data.Repository
{
    public class RepositoryFactory : IRepositoryFactory
    {
        private readonly IServiceProvider _serviceProvider;

        public RepositoryFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        
        public IApplicationRepository BuildApplicationRepository()
        {
            var service = _serviceProvider.GetService<IApplicationRepository>();

            return service;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用所描述的模式可以解决“数据库上下文是通过依赖注入创建的”错误,并且不需要 Reload()/Refresh() 方法。


Mik*_*ill 7

哦,这个问题困扰了我好几天。

我将Visual Studio 2017与.Net Core 2.1结合使用,我的EF Core代码看起来像这样:

//  1.  Load a [User] record from our database 
int chosenUserID = 12345;
User usr = dbContext.Users.FirstOrDefault(s => s.UserID == chosenUserID);

//  2. Call a web service, which updates that [User] record
HttpClient client = new HttpClient()
await client.PostAsync("http://someUrl", someContent);

//  3. Attempt to load an updated copy of the [User] record
User updatedUser = dbContext.Users.FirstOrDefault(s => s.UserID == chosenUserID);
Run Code Online (Sandbox Code Playgroud)

在第3步中,只需将“ updatedUser”设置为[User]记录的原始版本,而不是尝试加载新副本。因此,如果在步骤3之后,我修改了该[User]记录,那么实际上我将丢失该Web服务对其应用的任何设置。

我-最终-找到了两个解决方案。

我可以更改ChangeTracker设置。这行得通,但是我担心这样做的副作用:

dbContext.ChangeTracker.QueryTrackingBehavior = Microsoft.EntityFrameworkCore.QueryTrackingBehavior.NoTracking;
Run Code Online (Sandbox Code Playgroud)

或者,在尝试重新加载[User]记录之前,我可以插入以下命令...

await dbContext.Entry(usr).ReloadAsync();
Run Code Online (Sandbox Code Playgroud)

这似乎迫使.Net Core重新加载该[User]记录,并且生活再次美好。

我希望这是有用的...

跟踪并修复此错误花了我几天的时间。

还有描述不同的方式来解决这个缓存的问题一个很好的文章在这里

  • 我知道那是多么的沮丧。但是,这是我的建议和当前的理解,在EF的设计中,DbContext对象是工作单元模式的实现。它应该是短命的,并且不能被其他翻译使用。您可以尝试使用另一个新的DbContext来执行第二个操作。:) (2认同)
  • 几乎晚了一年,但使用 Reload 的解决方案时,我遇到了另一个错误,即“在上一个操作完成之前,在此上下文上启动了第二个操作。这通常是由使用同一 DbContext 实例的不同线程引起的,但实例成员不是保证线程安全。这也可能是由于在客户端上评估嵌套查询引起的,如果是这种情况,请重写查询以避免嵌套调用。” 由于后续的电话。在这种情况下,ChangeTracker 可能更好,但我也不确定副作用。 (2认同)

Ogg*_*las 6

Reload并且ReloadAsync从那时起就可以使用Entity Framework Core 1.1

例子:

//test.Name is test1
var test = dbContext.Tests.FirstOrDefault();
test.Name = "test2";

//test.Name is now test1 again
dbContext.Entry(test).Reload();
Run Code Online (Sandbox Code Playgroud)

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.changetracking.entityentry.reload?view=efcore-1.1

  • @GertArnold 非常正确,鉴于 OP 声明“我只发现人们使用 Reload 方法,该方法在 EF core 中不可用”,我决定发布一个答案。我认为缺少一个简短而完整的示例。 (2认同)

小智 5

这个简单的序列刷新了 EFCore 5.0 下的整个上下文:

public void Refresh()
{
    (Context as DbContext).Database.CloseConnection();
    (Context as DbContext).Database.OpenConnection();
}
Run Code Online (Sandbox Code Playgroud)