You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core MVC使用服务类时遇“无法访问已释放对象”错误求助

Fixing "Cannot access a disposed object" in ASP.NET Core MVC when moving code to services

Let's break down exactly why you're hitting this error and walk through the fixes step by step:

Root Causes

Your issue stems from two critical problems in your code:

  1. Unawaited async void operations: You're using an async delegate inside List.ForEach(), which creates an async void method. Async void is dangerous here because it doesn't let you wait for the asynchronous work to complete—your controller action returns immediately, and ASP.NET Core disposes scoped services (like ApplicationDbContext and UserManager) before your background async code finishes running.
  2. Manual service instantiation: While not the direct cause, manually creating InitialMigrationService with new bypasses ASP.NET Core's dependency injection container, which is designed to manage service lifecycles correctly. This can lead to lifecycle mismatches if you're not careful.

Step-by-Step Fixes

1. Fix Async/Await Usage (Critical!)

First, eliminate the async void delegate in MigrateUserSetup. Replace the ForEach loop with a regular foreach loop that properly awaits each asynchronous operation:

private async Task MigrateUserSetup(DataDump dump)
{
    // Update Accounts property (safe sync operation since it's in-memory)
    dump.UserSetupList.ForEach(u => u.Accounts = true);

    // Replace async ForEach with awaitable foreach loop
    foreach (var u in dump.UserSetupList)
    {
        if (string.IsNullOrEmpty(u.Email))
            continue;

        var swUser = _context.SoftwareUser.SingleOrDefault(du => du.OldID == u.ID);
        if (swUser == null)
        {
            _context.SoftwareUser.Add(new Models.SoftwareUser 
            { 
                Name = u.Name 
                // Add your other fields here
            });
            // Use async SaveChanges to avoid blocking
            await _context.SaveChangesAsync();
            swUser = _context.SoftwareUser.SingleOrDefault(du => du.OldID == u.ID);
        }

        string userID = await EnsureUser(u.Password, u.Email, swUser.ID);
        await EnsureRole(userID, ConstantData.ConstUserRole);
    }
}

Then update your MigrateDataDump method to be async (since it now calls an async method):

public async Task<bool> MigrateDataDump(DataDump dump)
{
    await MigrateUserSetup(dump);
    return true;
}

Finally, update your controller action to await the service call:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpPost]
[Route("GetDumpData")]
public async Task<bool> GetDumpData([FromBody] ApiDataDumpInfo apiDataDumpInfo)
{
    // ... your existing code to get dumpBytes ...
    Models.InitialMigration.DataDump dataDump = Models.InitialMigration.DataDump.DeserialiseFromByteArray(dumpBytes);
    
    // Await the async service method
    await _initialMigrationService.MigrateDataDump(dataDump);
    return true;
}

2. Use Dependency Injection for Your Service

Instead of manually creating InitialMigrationService, let ASP.NET Core's DI container handle it. This ensures your service uses the same scoped instances of DbContext, UserManager, etc., as your controller, and their lifecycles are managed correctly.

First, update your controller's constructor to inject IInitialMigrationService:

private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
private readonly IConfiguration _configuration;
private readonly IInitialMigrationService _initialMigrationService;

public MigrationController(
    ApplicationDbContext context, 
    UserManager<ApplicationUser> userManager, 
    RoleManager<IdentityRole> roleManager, 
    IConfiguration configuration,
    IInitialMigrationService initialMigrationService) // Inject the interface
{
    _context = context;
    _userManager = userManager;
    _roleManager = roleManager;
    _configuration = configuration;
    _initialMigrationService = initialMigrationService; // No more new!
}

Next, register your service with the DI container:

  • For .NET 6+ (Program.cs):
    builder.Services.AddScoped<IInitialMigrationService, InitialMigrationService>();
    
  • For .NET 5 or earlier (Startup.cs, inside ConfigureServices):
    services.AddScoped<IInitialMigrationService, InitialMigrationService>();
    

3. Bonus: Avoid Blocking Async Calls

Ensure you're never using .Result or .Wait() on async methods (like those from UserManager). You're already using await correctly in EnsureUser and EnsureRole—keep that up! Blocking async calls can lead to deadlocks and similar disposal issues.

Why This Works

By fixing the async/await pattern, you ensure all asynchronous work completes before the controller action finishes, so ASP.NET Core doesn't dispose your scoped services prematurely. Using DI ensures your service gets properly managed instances of dependencies that align with the controller's lifecycle.

内容的提问来源于stack exchange,提问作者Shaggs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:33:36