注入 DbContext 时无法访问 ASP.NET Core 中已释放的对象
本文关键字:Core 释放 对象 NET ASP DbContext 访问 注入 | 更新日期: 2023-09-27 17:59:27
在一个 ASP.NET 核心项目中,我在启动时有以下内容:
services.AddDbContext<Context>(x => x.UseSqlServer(connectionString));
services.AddTransient<IValidationService, ValidationService>();
services.AddTransient<IValidator<Model>, ModelValidator>();
验证服务如下:
public interface IValidationService {
Task<List<Error>> ValidateAsync<T>(T model);
}
public class ValidationService : IValidationService {
private readonly IServiceProvider _provider;
public ValidationService(IServiceProvider provider) {
_provider = provider;
}
public async Task<List<Error>> ValidateAsync<T>(T model) {
IValidator<T> validator = _provider.GetRequiredService<IValidator<T>>();
return await validator.ValidateAsync(model);
}
}
模型验证器如下:
public class ModelValidator : AbstractValidator<Model> {
public ModelValidator(Context context) {
// Some code using context
}
}
当我在控制器中注入 IValidationService 并将其用作:
List<Error> errors = await _validator.ValidateAsync(order);
我收到错误:
System.ObjectDisposedException:无法访问已释放的对象。一个 此错误的常见原因是释放已解决的上下文 从依赖注入,然后尝试使用它 应用程序中其他位置的上下文实例。 这可能会发生在你 在上下文中调用 Dispose((,或将上下文包装在 using 语句。 如果您使用的是依赖注入,则应 让依赖注入容器负责释放上下文 实例。对象名称:"上下文"。
知道为什么我在模型验证器中使用上下文时会出现此错误。
如何解决这个问题?
更新
所以我将代码更改为:
services.AddScoped<IValidationService, ValidationService>();
services.AddScoped<IValidator<Model>, ModelValidator>();
但是我得到同样的错误...
更新 - 启动时配置方法中的种子数据代码
所以在配置方法我有:
if (hostingEnvironment.IsDevelopment())
applicationBuilder.SeedData();
SeedData扩展名为:
public static class DataSeedExtensions {
private static IServiceProvider _provider;
public static void SeedData(this IApplicationBuilder builder) {
_provider = builder.ApplicationServices;
_type = type;
using (Context context = (Context)_provider.GetService<Context>()) {
await context.Database.MigrateAsync();
// Insert data code
}
}
我错过了什么?
更新 - 可能的解决方案
将我的种子方法更改为以下内容似乎有效:
using (IServiceScope scope =
_provider.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
Context context = _provider.GetService<Context>();
// Insert data in database
}
只是猜测导致错误的原因:
您正在使用 DI 和异步调用。如果在调用堆栈中的某个位置返回 void 而不是 Task,则会得到所描述的行为。此时,调用结束并释放上下文。因此,请检查您是否有一个异步调用返回 void 而不是 Task。如果更改返回值,则 ObjectDisposedException 可能是固定的。
public static class DataSeedExtensions {
private static IServiceProvider _provider;
public static async Task SeedData(this IApplicationBuilder builder) { //This line of code
_provider = builder.ApplicationServices;
_type = type;
using (Context context = (Context)_provider.GetService<Context>()) {
await context.Database.MigrateAsync();
// Insert data code
}
}
并在配置中:
if (hostingEnvironment.IsDevelopment()){
await applicationBuilder.SeedData();
}
有关如何修复此错误的博客文章:注入 DbContext 时无法访问 ASP.NET Core 中已释放的对象
我在使用内核时遇到了类似的问题 asp.net。我的控制器中有一个异步 POST 方法,当它返回 void 时,我将出现此异常。在我更改了 POST 方法返回任务后,问题解决了。
更改自:
public async void PostAsync([FromBody] Model yourmodel)
自
public async Task PostAsync([FromBody] Model yourmodel)
ASP.NET Core 2.1 的更新
在 ASP.NET Core 2.1中,方法略有变化。一般方法与 2.0 类似,只是方法名称和返回类型已更改。
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.Seed();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return new WebHostBuilder()
...; // Do not call .Build() here
}
适用于 ASP.NET 酷睿2.0
在 ASP.NET Core 2.0 中,EF Core 工具(dotnet ef migrations
等(在设计时确定 DbContext 和连接字符串的方式发生了一些变化。
以下答案表明,在调用任何dotnet ef xxx
命令时,将应用迁移和种子设定。
获取 EF Core 工具的设计时实例的新模式是使用 BuildHostWeb
静态方法。
根据此公告,EF Core 现在将使用静态 BuildWebHost
方法配置整个应用程序,但不运行它。
public class Program { public static void Main(string[] args) { var host = BuildWebHost(args); host.Run(); } // Tools will use this to get application services public static IWebHost BuildWebHost(string[] args) => new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build(); }
在旧的Main
方法中替换它
public static void Main(string[] args)
{
var host = BuildWebHost(args)
.Seed();
host.Run();
}
其中种子是扩展方法:
public static IWebHost Seed(this IWebHost webhost)
{
using (var scope = webhost.Services.GetService<IServiceScopeFactory>().CreateScope())
{
// alternatively resolve UserManager instead and pass that if only think you want to seed are the users
using (var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
{
SeedData.SeedAsync(dbContext).GetAwaiter().GetResult();
}
}
}
public static class SeedData
{
public static async Task SeedAsync(ApplicationDbContext dbContext)
{
dbContext.Users.Add(new User { Id = 1, Username = "admin", PasswordHash = ... });
}
}
旧答案,仍然适用于 ASP.NET 酷睿1.x
关于如何在应应用的核心应用程序中播种实体框架核心 ASP.NET 有一种半官方模式,因为在应用程序启动期间没有请求,因此没有RequestServices
(解析作用域服务(。
实质上,它归结为创建一个新范围,解析所需的类型,并在完成后再次释放范围。
// serviceProvider is app.ApplicationServices from Configure(IApplicationBuilder app) method
using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var db = serviceScope.ServiceProvider.GetService<AppDbContext>();
if (await db.Database.EnsureCreatedAsync())
{
await SeedDatabase(db);
}
}
通过app.ApplicationServices.GetService<MyService>()
直接解析服务的原因之一是ApplicationServices
是应用程序(或生存期(范围提供程序,此处解析的服务在应用程序关闭之前保持活动状态。
通常,作用域容器将从其父容器解析(如果对象已存在(。因此,如果在应用程序中以这种方式实例化 DbContext,它将ApplicationServices
容器中可用,当请求发生时,将创建一个子容器。
现在,在解析 DbContext 时,它不会解析为作用域,因为它已存在于父容器中,因此将改为返回父容器的实例。但由于它在播种期间已被处理,因此无法访问。
作用域容器不是别的,而是生存期有限的单例容器。
因此,切勿使用上述模式(首先创建作用域并从中解析(在应用程序启动中解析作用域服务。
正在使用任何async void
请将其替换为async Task
有同样的问题。希望这对某人有所帮助。除了使方法async
并返回Task
之外,您还需要确保无论在哪里调用该方法,都将
和张杨类似,我不得不改变我的控制器功能从:
public IActionResult MyFunc([FromBody]string apiKey)
自:
public async Task<IActionResult> MyFunc([FromBody]string apiKey)
DBContext 默认情况下是按请求限定的,但您有依赖于它的东西作用域为瞬态,因此它们没有相同的作用域,并且 DBContext 可能会在您使用它之前被释放
我想为那些尝试在控制器中启动后台任务的人分享我的解决方案。这意味着您希望启动一个任务,并且不想等待结果(如将审核日志记录到数据库(。如果您正在创建一个任务并尝试在该任务中执行数据库操作,您将收到此错误;
无法访问已释放的对象。此错误的常见原因是释放了通过依赖项注入解析的上下文,然后尝试在应用程序的其他位置使用相同的上下文实例。如果在上下文中调用 Dispose(( 或将上下文包装在 using 语句中,则可能会发生这种情况。如果使用依赖关系注入,则应让依赖关系注入容器负责释放上下文实例。''r'对象名称:"DBContext"。
已经详细解释了。在这里找到它
就我而言,这不是异步问题,但代码有一个using (DataContext dc=dataContext) {}
块,当然,上下文在那之后被处理掉了。
在我的情况下,控制器方法是异步的,它返回一个任务,但在里面我有 2 个等待调用。第一个等待调用从服务获取一些数据,第二个等待调用使用 EF 写入数据库。我不得不从第二个调用中删除等待,然后它才起作用。我没有从方法签名中删除异步/等待。我只是在没有等待的情况下调用了第二种方法。
我遇到了类似的错误,后来能够解决它。
我在不使用 await
的情况下调用异步方法.
旧代码
var newUser = _repo.Register(newUserToCreate);
修复后
var newUser = await _repo.Register(newUserToCreate);