我正在使用EntityFramework.Extended库来执行批量更新。唯一的问题是EF无法跟踪库执行的批量更新。因此,当我再次查询DbContext
时,它不会返回更新的实体。
我发现在查询时使用AsNoTracking()
方法会禁用跟踪并从数据库中获取新数据。但是,由于EF无法跟踪使用AsNoTracking()
查询的实体,因此无法对查询的数据执行任何更新。
有没有办法强制EF在跟踪更改时获取最新数据?
答案 0 :(得分:123)
请尝试刷新单个实体:
Context.Entry<T>(entity).Reload()
修改强>
要获取实体集合的新数据,请务必在每次请求后部署DbContext
实例。
答案 1 :(得分:12)
我在搜索我遇到的问题的解决方案时偶然发现了这个问题,在更新实体后导航属性没有填充。每当我尝试从数据库重新加载实体时,它都会从本地存储中获取条目,而不会通过延迟加载来填充导航属性。我没有破坏上下文并重新创建一个上下文,而是让我能够在代理工作的情况下获取新数据:
_db.Entry(entity).State = EntityState.Detached;
它背后的逻辑是 - 我的更新附加了实体,因此它会跟踪对它的更改。这会将其添加到本地商店。此后,任何使用功能代理检索实体的尝试都会导致它获取本地实体,而不是转到数据库并返回一个新的,启用代理的实体。我尝试了上面的重载选项,它确实从数据库中刷新了对象,但这并没有为您提供延迟加载的代理对象。我试过做Find(id), Where(t => t.Id = id), First(t => t.Id = id)
。最后,我检查了提供的可用状态,并看到存在“分离”状态。找到了!希望这有助于某人。
答案 2 :(得分:1)
使代码在相同的上下文中运行不会产生更新的实体。它只会在运行之间附加在数据库中创建的新实体。 EF力重装可以这样做:
ObjectQuery _query = Entity.MyEntity;
_query.MergeOption = MergeOption.OverwriteChanges;
var myEntity = _query.Where(x => x.Id > 0).ToList();
答案 3 :(得分:1)
我声明了实体变量,没有赋值,作为类的一部分。这允许我处理一个实例而不会丢失变量以供其他方法参考。我刚刚遇到过这个问题,所以它在它的带下没有很多运行时间,但到目前为止似乎工作正常。
public partial class frmMyForm
{
private My_Entities entity;
public frmMyForm()
{
InitializeComponent();
}
private void SomeControl_Click(object sender, EventArgs e)
{
db.SaveChanges();
db.Dispose();
entity = new My_Entities();
//more code using entity ...
}
答案 4 :(得分:1)
偶然发现了这个问题。我的应用没有从数据库返回新数据。
这似乎是 3 个解决方案:
选择时重新加载:首先选择对象,然后重新加载。如果没有缓存,加载两次?
使用后分离:如果您在使用后忘记分离对象,则会导致应用程序中完全独立的部分出现错误,而这些错误将极难追踪。
使用后处理 DbContext。绝对是要走的路。
我在 Repository 类中创建了我的 DbContext 实例。如果 DbContext 是在存储库级别声明的,那么我无法控制它的处理方式。这是一个禁忌。如果我在每次调用时都创建一个新的 DbContext,那么我就不能调用 Select、修改数据,然后调用 Update。
似乎我的存储库模式从根本上缺少某些东西。
在对基础知识库模式进行了一些研究后,我找到了解决方案:工作单元模式和知识库模式。
This is an excellent article on the Unit of Work pattern
或this article from Microsoft。我目前拥有的是页面中进一步的存储库,缺少的是“实现通用存储库和工作单元类”部分
基本上,您不是将存储库注入到您的服务中,而是通过注入到您的服务中的 UnitOfWork 来访问所有存储库。它将解决很多问题。
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationContext _context;
public UnitOfWork(ApplicationContext context)
{
_context = context;
Developers = new DeveloperRepository(_context);
Projects = new ProjectRepository(_context);
}
public IDeveloperRepository Developers { get; private set; }
public IProjectRepository Projects { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
问题仍然存在:如何创建 IUnitOfWork 实例?
如果我在类构造函数中创建它,就像存储库一样注入,那么它的创建和销毁方式完全相同,我们又回到了同样的问题。在 ASP.NET 和 MVC 中,类实例的生命周期很短,因此在构造函数中注入可能没问题,但在 Blazor 和桌面应用程序中,类实例的生命周期要长得多,而且问题更多。
This article from Microsoft 明确指出依赖注入不适合在 Blazor 中管理 DbContext 的生命周期:
<块引用>在 Blazor Server 应用中,范围服务注册可能会出现问题 因为实例在用户内部的组件之间共享 电路。 DbContext 不是线程安全的,也不是为并发而设计的 采用。由于以下原因,现有的生命周期是不合适的:
他们建议使用工厂模式,可以像这样实现
/// <summary>
/// Creates instances of UnitOfWork. Repositories and UnitOfWork are not automatically injected through dependency injection,
/// and this class is the only one injected into classes to give access to the rest.
/// </summary>
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private readonly IDateTimeService _dateService;
private readonly DbContextOptions<PaymentsContext> _options;
public UnitOfWorkFactory(IDateTimeService dateService, DbContextOptions<PaymentsContext> options)
{
_dateService = dateService;
_options = options;
}
/// <summary>
/// Creates a new Unit of Work, which can be viewed as a transaction. It provides access to all data repositories.
/// </summary>
/// <returns>The new Unit of Work.</returns>
public IUnitOfWork Create() => new UnitOfWork(CreateContext(), _dateService);
/// <summary>
/// Creates a new DbContext.
/// </summary>
/// <returns>The new DbContext.</returns>
public PaymentsContext CreateContext() => new(_options);
}
IWorkOfUnit 和任何存储库都不会注册到 IoC 容器中。只有 IWorkOfUnitFactory。
最后……如何在各种服务之间共享事务?
我有一个 SetStatus 方法来更新数据库中的状态字段。这种方法应该如何知道它是独立操作还是较大事务的一部分?
由于类级别的依赖注入不适合管理和共享单元的工作,那么唯一的选择就是将其作为参数传递给需要它的方法。
我为每个需要它的方法添加了一个可选的 IUnitOfWork? workTransaction = null
参数,并且仅当此参数为空时才调用 Save。这是一个实现。
public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workTransaction = null)
{
using var unitOfWork = _workFactory.Create();
var work = workTransaction ?? unitOfWork;
var order = await work.Orders.GetByIdAsync(orderId).ConfigureAwait(false);
if (order != null)
{
order.Status = status;
work.Orders.Update(order); // DateModified gets set here
if (workTransaction == null)
{
await work.SaveAsync();
}
}
return order;
}
另一种选择是让 IUnitOfWorkFactory.Create 接受 workTransaction 参数,并在设置时:
呸!那个错误是一个兔子洞。这是一个很长的解决方案,但应该永远解决它。
答案 5 :(得分:0)
对我来说...... 我像这样访问我的DbContext:
_viewModel.Repo.Context
要强制EF命中数据库,我会这样做:
_viewModel.Repo.Context = new NewDispatchContext();
使用新实例覆盖当前的DbContext。然后,下次我使用我的数据服务时,他们会从数据库中获取数据。
答案 6 :(得分:0)
重新加载特定实体对我来说不是一个选择,因为我不知道确切的实体。我也不想创建一个新的 DbContext,因为它是由 DI 注入的。所以我采用了以下技巧来“重置”整个上下文。
foreach (var entry in db.ChangeTracker.Entries())
{
entry.State = EntityState.Detached;
}