Вопрос или проблема
У меня есть два похожих проекта (потому что я хотел один использовать для тестирования, но на самом деле ничего не меняется от другого), в первом все работает отлично, когда я заполняю 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.RoleStore
5.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
.
Если описанные выше шаги не решают проблему, вы можете рассмотрите возможность создания минимального примера, воспроизводящего эту ошибку, и обратитесь к сообществу разработчиков для дальнейшей помощи.