首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >注入ASP.NET时无法访问DbContext内核中已释放的对象

注入ASP.NET时无法访问DbContext内核中已释放的对象
EN

Stack Overflow用户
提问于 2016-08-01 16:44:49
回答 11查看 116.1K关注 0票数 47

在一个ASP.NET核心项目中,我有以下关于启动的内容:

代码语言:javascript
运行
复制
  services.AddDbContext<Context>(x => x.UseSqlServer(connectionString));

  services.AddTransient<IValidationService, ValidationService>();

  services.AddTransient<IValidator<Model>, ModelValidator>();

ValidationService如下:

代码语言:javascript
运行
复制
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);
    }
}

ModelValidator如下:

代码语言:javascript
运行
复制
public class ModelValidator : AbstractValidator<Model> {
  public ModelValidator(Context context) {
    // Some code using context
  }
}

当我将IValidationService注入控制器并将其用作:

代码语言:javascript
运行
复制
List<Error> errors = await _validator.ValidateAsync(order);    

我知道错误:

System.ObjectDisposedException:无法访问已释放的对象。造成此错误的一个常见原因是处理通过依赖项注入解决的上下文,然后尝试在应用程序的其他位置使用相同的上下文实例。这可能发生在对上下文调用This (),或者将上下文包装在using语句中。如果您使用的是依赖注入,则应该让依赖注入容器负责处理上下文实例。对象名称:“上下文”。

知道为什么在ModelValidator中使用上下文时会出现此错误。

怎么解决这个问题?

更新

所以我把代码改为:

代码语言:javascript
运行
复制
services.AddScoped<IValidationService, ValidationService>();

services.AddScoped<IValidator<Model>, ModelValidator>();

但我也犯了同样的错误。

更新种子数据代码在启动上的配置方法

因此,关于配置方法,我有:

代码语言:javascript
运行
复制
if (hostingEnvironment.IsDevelopment())
  applicationBuilder.SeedData();

SeedData扩展是:

代码语言:javascript
运行
复制
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
    }
}

我遗漏了什么?

更新--一个可能的解决方案

将我的种子方法改为以下方法似乎是可行的:

代码语言:javascript
运行
复制
using (IServiceScope scope = 
    _provider.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
    Context context = _provider.GetService<Context>();
    // Insert data in database
}
EN

回答 11

Stack Overflow用户

回答已采纳

发布于 2016-08-01 17:31:15

更新ASP.NET核心2.1

在ASP.NET Core2.1中,方法略有改变。通用方法与2.0类似,只是方法名称和返回类型已经更改。

代码语言:javascript
运行
复制
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 Core2.0

在ASP.NET Core2.0中,etc (dotnet ef migrations等)的使用方式发生了一些变化。在设计时确定DbContext和连接字符串。

下面的答案将导致在调用任何dotnet ef xxx命令时应用迁移和种子。

获取EF核心工具设计时实例的新模式是使用BuildHostWeb静态方法。

根据本公告,EF现在将使用静态BuildWebHost方法来配置整个应用程序,但不运行它。

公共类程序{公共静态空主(string[] args) { var host = BuildWebHost(args);host.Run();} //工具将使用此方法获取应用程序服务--公共静态.UseContentRoot(Directory.GetCurrentDirectory()) IWebHost BuildWebHost(string[] args) =>新WebHostBuilder() .UseKestrel() .UseKestrel .UseIISIntegration() .UseStartup() .Build();}

在旧的Main方法中替换它

代码语言:javascript
运行
复制
public static void Main(string[] args)
{
    var host = BuildWebHost(args)
        .Seed();

    host.Run();
}

其中种子是一种扩展方法:

代码语言:javascript
运行
复制
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 (解决范围内的服务)。

本质上,它归结为创建一个新的作用域,解析所需的类型,并在完成后再次释放该作用域。

代码语言:javascript
运行
复制
// 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时,它不会被解析为作用域,因为它已经存在于父容器中,因此将返回父容器的实例。但由于它已在播种期间处置,它将无法访问。

一个作用域容器就是一个寿命有限的单例容器。

因此,不要使用上面的模式解析应用程序启动w/o中的作用域服务,首先创建一个作用域并从中解析。

票数 23
EN

Stack Overflow用户

发布于 2017-05-09 17:44:33

只是猜测一下是什么导致了您的错误:

您正在使用DI和异步调用。如果调用堆栈中的某个位置返回了一个空值,而不是任务,那么您将得到所描述的行为。此时,调用结束,上下文被释放。因此,请检查是否有一个异步调用返回一个空而不是任务。如果更改返回值,则ObjectDisposedException可能是固定的。

代码语言:javascript
运行
复制
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

  }

}

在配置中:

代码语言:javascript
运行
复制
if (hostingEnvironment.IsDevelopment()){
   await  applicationBuilder.SeedData();
}

关于如何修复此错误的博客文章:注入ASP.NET时无法访问DbContext内核中已释放的对象

票数 113
EN

Stack Overflow用户

发布于 2018-02-18 05:14:59

我在使用asp.net内核时也遇到了类似的问题。我的控制器中有一个异步POST方法,当它返回when时,我将有这个异常。在我更改POST方法后,返回一个任务,问题就解决了。

更改如下:

代码语言:javascript
运行
复制
public async void PostAsync([FromBody] Model yourmodel)

代码语言:javascript
运行
复制
public async Task PostAsync([FromBody] Model yourmodel)
票数 37
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38704025

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档