我在我的项目中使用实体框架,并且在流程项目中的某个点,它调用第二个系统来处理和更新数据库中的一些信息。
但是当第二个系统完成时,我的项目尝试再次从数据库获取此信息,但它返回旧信息。
我将 .NET 7.0 与 Entity Framework 6 和 SqLite 3.0 结合使用。
这是我的代码的一部分:
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream("Totem.App.appsettings.json");
var config = new ConfigurationBuilder()
.AddJsonStream(stream)
.Build();
builder.Configuration.AddConfiguration(config);
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(DesignTimeDbContextFactory.GetConnectionString(config))
.Options;
var factory = new PooledDbContextFactory<AppDbContext>(options);
var context = factory.CreateDbContext();
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlite(DesignTimeDbContextFactory.GetConnectionString(config)));
builder.Services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>));
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ICategoryService, CategoryService>();
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<IBannerService, BannerService>();
builder.Services.AddScoped<IAuthorService, AuthorService>();
builder.Services.AddScoped<IConfigService, ConfigService>();
builder.Services.AddScoped<IMeasureService, MeasureService>();
builder.Services.AddScoped<ICartService, CartService>();
builder.Services.AddScoped<IItemCartService, ItemCartService>();
builder.Services.AddScoped<IBranchService, BranchService>();
这是我的
BaseRepository
:
public class BaseRepository<TEntity> : IDisposable, IBaseRepository<TEntity> where TEntity : class
{
protected readonly AppDbContext _context;
#region constructors
public BaseRepository(AppDbContext context)
{
_context = context;
}
#endregion
public void Dispose()
{
_context.Dispose();
GC.SuppressFinalize(true);
}
public virtual void Insert(TEntity obj)
{
_context.Set<TEntity>().Add(obj);
}
public virtual void Update(TEntity obj)
{
_context.Entry(obj).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
}
public virtual void Delete(int id)
{
_context.Set<TEntity>().Remove(GetById(id));
}
public virtual IQueryable<TEntity> GetAll()
{
return _context.Set<TEntity>();
}
public virtual TEntity GetById(int id)
{
return _context.Set<TEntity>().Find(id);
}
public virtual void SaveChanges()
{
_context.SaveChanges();
}
}
我尝试使用
AddTransient
和AddSingleton
,但这没有解决。
这几乎肯定是第一种情况下重复读取的 DbContext 的范围涵盖更新数据的第二个进程的操作,因此当您再次获取记录时,您只是获取跟踪缓存中的内容.
我不知道这一行的意义是什么:
var context = factory.CreateDbContext();
这可能只是为了检查您是否可以实例化 DbContext,但如果将该引用注入到任何地方,它将被视为 Singleton,这不太好。
我强烈建议的下一件事是摆脱 BaseRepository。这是一个通用存储库模式,是 EF 的反模式。查看代码,它是 DbContext 上的一个贫血的外观。只需使用 DbContext 即可。在处理生命周期范围内注入的 DbContext 时,您也不希望代码处理容器提供的引用,这是容器的工作,除非您已将引用定义为瞬态且由外部拥有。如果服务/存储库的范围较长,则将 DbContext 注册更改为 Transient 将无济于事。当容器构造这些服务时,它将解析 DbContext,Transient 将确保它们每个都获得自己的 DbContext 实例,但该实例与引用类一样长。
问题的核心:EF 的跟踪缓存。默认情况下使用此选项,以便 EF 可以跟踪对实体所做的更改,并确保对同一实体的多个请求收到相同的实例。这在处理更新场景时非常有用,但在读取您期望可能由其他进程在后台修改的数据时,就会对您不利。对于您想要确保获取最新数据的读取操作,并且由于您没有编辑该数据,因此不需要跟踪任何更改,请在查询中使用
AsNoTracking()
:
例如读取ItemCarts的当前数据状态:
var carts = await ItemCarts
.AsNoTracking()
.Where(x => /* criteria */)
.ToListAsync();
此查询将始终从数据库中提取数据状态。检索到的实体不会放入跟踪缓存中(使其更快一些),并且也不会读取可能恰好位于跟踪缓存中的任何实体。