Sar*_*ana 79 c# asp.net-mvc entity-framework dbcontext
我正在使用EntityFramework.Extended库来执行批量更新.唯一的问题是EF无法跟踪库执行的批量更新.因此,当我DbContext再次查询时,它不会返回更新的实体.
我发现AsNoTracking()在查询时使用方法会禁用跟踪并从数据库中获取新数据.但是,由于EF不跟踪查询的实体AsNoTracking(),因此无法对查询的数据执行任何更新.
有没有办法强制EF在跟踪变化时获取最新数据?
PlT*_*lor 138
请尝试刷新单个实体:
Context.Entry<T>(entity).Reload()
Run Code Online (Sandbox Code Playgroud)
编辑:
要获取实体集合的新数据,请务必DbContext在每次请求后处理实例.
Zac*_*ach 15
我在寻找解决我遇到的问题的解决方案时偶然发现了这个问题,在更新实体后导航属性没有填充.每当我尝试从数据库重新加载实体时,它都会从本地存储中获取条目,而不会通过延迟加载来填充导航属性.我没有破坏上下文并重新创建上下文,而是发现这允许我在代理工作时获取新数据:
_db.Entry(entity).State = EntityState.Detached;
Run Code Online (Sandbox Code Playgroud)
它背后的逻辑是 - 我的更新附加了实体,因此它将跟踪它的变化.这会将其添加到本地商店.此后,任何使用功能代理检索实体的尝试都会导致它获取本地实体,而不是转到数据库并返回一个新的,启用代理的实体.我尝试了上面的重载选项,它确实从数据库中刷新了对象,但这并没有为您提供延迟加载的代理对象.我试过做一个Find(id), Where(t => t.Id = id), First(t => t.Id = id).最后,我检查了提供的可用状态,并看到存在"分离"状态.找到了!希望这有助于某人.
偶然发现这个问题。我的应用程序没有从数据库返回新数据。
这些似乎是 3 个解决方案:
选择时重新加载:首先选择对象,然后重新加载。如果没有缓存就加载两次?
使用后分离:如果您忘记在使用后分离对象,则会在应用程序的完全独立的部分中导致错误,而这些错误将非常难以追踪。
使用后处置 DbContext。看起来绝对是可行的方法。
我正在 Repository 类中创建 DbContext 实例。如果 DbContext 是在存储库级别声明的,那么我无法控制它的处置方式。这是一个禁忌。如果我在每次调用时创建一个新的 DbContext,那么我无法调用 Select、修改数据,然后调用 Update。
似乎我的存储库模式中根本缺少某些东西。
经过对基本存储库模式的一些研究后,我找到了解决方案:工作单元模式和存储库模式。
或者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();
}
}
Run Code Online (Sandbox Code Playgroud)
剩下的问题是:如何创建 IUnitOfWork 实例?
如果我在类构造函数中创建它,就像存储库一样注入,那么它的创建和销毁方式完全相同,我们又回到了同样的问题。在 ASP.NET 和 MVC 中,类实例的生命周期很短,因此在构造函数中注入可能没问题,但在 Blazor 和桌面应用程序中,类实例的生命周期要长得多,这也是一个问题。
Microsoft 的这篇文章明确指出依赖注入不适合在 Blazor 中管理 DbContext 的生命周期:
在 Blazor Server 应用中,范围服务注册可能会出现问题,因为实例是在用户电路内的组件之间共享的。DbContext 不是线程安全的,也不是为并发使用而设计的。由于以下原因,现有的生命周期是不合适的:
- Singleton 在应用程序的所有用户之间共享状态并导致不适当的并发使用。
- 作用域(默认值)在同一用户的组件之间带来了类似的问题。
- 每个请求都会产生一个新实例的瞬态结果;但由于组件的寿命可能很长,这会导致上下文的寿命比预期的要长。
他们建议使用工厂模式,可以这样实现
/// <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);
}
Run Code Online (Sandbox Code Playgroud)
IWorkOfUnit 和任何存储库都不会注册到 IoC 容器中。只有 IWorkOfUnitFactory。
最后...如何在各种服务之间共享事务?
我有一个 SetStatus 方法来更新数据库中的状态字段。该方法如何知道它是独立操作还是较大事务的一部分?
由于类级依赖注入不适合管理和共享单元的工作,因此唯一的选择是将其作为参数传递给需要它的方法。
IUnitOfWork? workScope = null我向每个需要它的方法添加一个可选参数,并且仅当该参数为 null 时才调用 Save。这是一个实现。
public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
using var unitOfWork = _workFactory.Create();
var work = workScope ?? unitOfWork;
var order = await work.Orders.GetByIdAsync(orderId);
if (order != null)
{
order.Status = status;
work.Orders.Update(order); // DateModified gets set here
if (workScope == null)
{
await work.SaveAsync();
}
}
return order;
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是让 IUnitOfWorkFactory.Create 获取 workScope 参数,并在设置时:
我的最终实现可以这样使用
public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
using var unitOfWork = _workFactory.Create(workScope);
var order = await unitOfWork.Orders.GetByIdAsync(orderId);
if (order != null)
{
order.Status = status;
work.Orders.Update(order); // DateModified gets set here
await unitOfWork.SaveAsync(); // Ignored if workScope != null
}
return order;
}
Run Code Online (Sandbox Code Playgroud)
唷!那个虫子就是一个兔子洞。这是一个相当长的解决方案,但应该通过可靠的架构来解决它。
| 归档时间: |
|
| 查看次数: |
72183 次 |
| 最近记录: |