“Не удалось получить доступ к уничтоженному экземпляру контекста” — это только что началось, нет внедрения DBContext или ошибок async void.

Вопрос или проблема

У меня есть два похожих проекта (потому что я хотел один использовать для тестирования, но на самом деле ничего не меняется от другого), в первом все работает отлично, когда я заполняю User и Roles при запуске, но во втором есть проблема, которую я не могу понять, и я получаю следующую ошибку:

Users = {“Не удалось получить доступ к удаленному экземпляру контекста. Одна из распространенных причин этой ошибки – это удаление экземпляра контекста, который был получен из внедрения зависимостей, а затем попытка использовать тот же экземпляр контекста в другом месте вашего приложения. Это может произойти, если вы вызываете ‘Dispose’ для экземпляра контекста или оборачиваете его в оператор using. Если вы используете внедрение зависимостей, вам следует позволить контейнеру внедрения зависимостей позаботиться о закрытии экземпляров контекста.\r\nИмя объекта: ‘AppDbContext’.”}

Это происходит, когда я запускаю 2-й проект, но не происходило в предыдущем, зная, что я ничего не изменял.

Я читал, что эта проблема возникает, когда контроллер или метод возвращают async void, но нигде в моем коде нет метода, который возвращает это, только async Task<T>/async Task.

Я добавил комментарии, где отладчик показывает, что проблема возникает:

CreateRoles.cs

public static class CreateRoles
{
    public static async Task SeedAsync(
        UserManager<ApplicationUser> userManager,
        RoleManager<ApplicationRole> roleManager,
        IConfiguration configuration,
        IPasswordHasher passwordHasher)
    {
        IdentityResult roleResult;

        var applicationRoleAdmin = new ApplicationRole
        {
            Id = Guid.Parse("cb0dc828-2755-4b3f-9035-788839dcc8cf"),
            Name = "Admin"
        };

    var applicationRoleUser = new ApplicationRole
    {
        Id = Guid.Parse("17fdc6f4-bffe-45e6-9ca6-adb0af6ffcc3"),
        Name = "User"
    };

    // ЗДЕСЬ ПРОИЗОШЛА ПРОБЛЕМА, она на adminRoleExist, что нарушает выход из этого класса
    // и возвращается в Program.cs, что никогда не происходило в первом проекте <-------------
    var adminRoleExist = await roleManager.RoleExistsAsync("Admin");
    if (!adminRoleExist)
    {
        roleResult = await roleManager.CreateAsync(applicationRoleAdmin);
    }

    var userRoleExist = await roleManager.RoleExistsAsync("User");
    if (!userRoleExist)
    {
        roleResult = await roleManager.CreateAsync(applicationRoleUser);
    }

    var applicationUser = new ApplicationUser
    {
        Id = Guid.NewGuid(),
        UserName = "whatever",
        FirstName = "whatever",
        LastName = "whatever",
        Email = "whatever",
        PhoneNumber = "whatever",
        CategoryId = whatever,
        SecurityStamp = Guid.NewGuid().ToString(),
        PasswordHash = passwordHasher.Hash(configuration["whatever"])
    };

    var user = await userManager.FindByIdAsync(applicationUser.Id.ToString());
    if (user == null)
    {
        try
        {
            var t = await userManager.CreateAsync(applicationUser);
            await userManager.AddToRoleAsync(applicationUser, "Admin");
            await userManager.AddToRoleAsync(applicationUser, "User");
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}
}

Program.cs

internal class Program
{
private static async Task Main(string[] args)
{
    IConfigurationRoot configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .Build();

    CancellationToken cancellationToken = new CancellationToken();

    IPasswordHasher passwordHasher = new PasswordHasher();

    var builder = WebApplication.CreateBuilder(args);

    var servicesSetup = builder.Services;
    servicesSetup.AddApplicationServices();
    servicesSetup.AddPersistenceServicesProduction(configuration);
    servicesSetup.AddControllers().AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    });
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();

    var app = builder.Build();

    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    using (var scope = app.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        await DataHelper.ManageDataAsync(services, cancellationToken);

        try
        {
            // ЗДЕСЬ ОТЛАДЧИК ПОКАЗЫВАЕТ ОШИБКУ, на userManager и переходит к оператору catch <-------
            var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
            var roleManager = services.GetRequiredService<RoleManager<ApplicationRole>>();

            await CreateRoles.SeedAsync(
                userManager,
                roleManager,
                configuration,
                passwordHasher);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    app.UseRouting();

    app.UseHttpsRedirection();

    app.UseAuthorization();

    app.MapControllers();

    app.Run();
}
}

Я знаю, что у вас нет первого проекта для сравнения, но я буквально скопировал и вставил и ничего не изменил, кроме пространства имен, потому что у него другое название.

Я пробовал без оператора using, но остался в той же ситуации.

Полный StackTrace:

at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.SetTEntity
at Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore5.get_Roles() at Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore5.FindByNameAsync(String normalizedName, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Identity.RoleManager`1.d__33.MoveNext()
at ColdStoneOfSM.Persistence.Seed.CreateRoles.d__0.MoveNext() in C:\Users\name\source\repos\My.Website\My.Persistence\Seed\CreateRoles.cs:line 30
at Program.d__0.MoveNext() in C:\Users\name\source\repos\My.Website\My.API\Program.cs:line 55

Ответ или решение

Проблема, с которой вы столкнулись, имеет обширную область возможных причин, но ключевым моментом является ошибка, возникающая из-за несанкционированного доступа к экземпляру контекста базы данных (DbContext) после его освобождения. Давайте рассмотрим это более подробно.

1. Понимание ошибки

Сообщение об ошибке "Cannot access a disposed context instance" (Невозможно получить доступ к экземпляру контекста, который был освобожден) указывает на то, что по какой-то причине экземпляр вашего AppDbContext был освобожден до того, как вы попытались выполнить запрос к базе данных. Это может произойти, если:

  • Контекст был заведомо освобожден (например, с использованием конструкции using).
  • Вызывается метод, который требует доступ к контексту после его удаления.

2. Возможные источники ошибки

2.1 Dependency Injection

Убедитесь, что вы правильно используете Dependency Injection (DI) для вашего DbContext. Если вы предоставляете контекст через DI и он освобождается в одном из сервисов, а затем используется в другом месте, возникает такая ошибка. В вашем случае это не выглядит проблемой, так как вы не наблюдаете явных ошибок в коде.

2.2 Уровни изоляции

Проверьте, как настроены ваши сервисы и их жизненные циклы. Если вы используете AddScoped для вашего DbContext, убедитесь, что все зависимости, потребляющие DbContext, также имеют Scoped уровень.

2.3 Метод SeedAsync

В методе SeedAsync вы работаете с UserManager и RoleManager, которые тоже зависят от DbContext. Если весь код выполняется в области видимости, где контекст уже был освобожден, это может вызвать проблему.

3. Проверка окружения

Обратите внимание на различия в конфигурации между двумя проектами. Иногда маленькие изменения в Startup.cs или в конфигурации DI могут привести к проблемам. Сравните:

  • Наличие нужных сервисов
  • Включение/исключение Middlewares
  • Секреты и конфигурацию

4. Программа Seed

В вашем методе Program.cs, в блоке с использованием var scope = app.Services.CreateScope(), убедитесь, что вы продолжаете использовать тот же самый scope для всех вызовов услуг, которые зависят от DbContext. Рассмотрите возможность использования async Task вместо прямого вызова await CreateRoles.SeedAsync(...), чтобы убедиться, что все завершилось, прежде чем программа продолжит выполнение.

5. Логи и Мониторинг

Добавьте логи для отслеживания создания и освобождения вашего DbContext. Используйте ILogger в вашем классе CreateRoles, чтобы фиксировать, когда контекст создается и освобождается, что может помочь вам выявить проблемы.

Заключение

Хотя вы и говорите, что не вносили изменения в код, внимательное изучение конфигурационных различий между проектами может выявить неожиданные нюансы, приводящие к данной ошибке. Убедитесь, что контекст базы данных остается доступным на всем протяжении жизненного цикла приложения и что все зависимости правильно настроены для работы в рамках одного и того же scope.

Если описанные выше шаги не решают проблему, вы можете рассмотрите возможность создания минимального примера, воспроизводящего эту ошибку, и обратитесь к сообществу разработчиков для дальнейшей помощи.

Оцените материал
Добавить комментарий

Капча загружается...