Я использую IdentityDbContext, и у меня есть класс SeedData, который я использую внутри метода OnModelCreating для заполнения базы данных с использованием начальных миграций следующим образом:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//
//
SeedData seedData = new(_userManager);
seedData.SeedWebsiteAdmins(modelBuilder.Entity<WebsiteAdmin>());
seedData.SeedCompanyAdmins(modelBuilder.Entity<CompanyAdmin>());
//
//
}
Этому классу SeedData требуется параметр типа UserManager внутри его конструктора, поскольку ему необходимо создавать новых пользователей следующим образом:
public async void SeedWebsiteAdmins(EntityTypeBuilder<WebsiteAdmin> entity)
{
var superAdminLogin = new ApplicationUser()
{
Name = "Name",
Email = "Email",
UserType = UserType.WebsiteAdmin
};
var result = await _userManager.CreateAsync(superAdminLogin, "{password}");
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(superAdminLogin, RoleNames.WebsiteAdminRole);
}
var superAdmin = new WebsiteAdmin()
{
Id = SuperAdminId,
WebsiteAdminLoginId = superAdminLogin.Id,
MaxServiceDiscount = 100,
};
entity.HasData(superAdmin);
}
Я попытался внедрить UserManager в конструктор DbContext, но это вызвало следующую ошибку:
System.InvalidOperationException: невозможно разрешить службу для типа «Microsoft.EntityFrameworkCore.DbContext» при попытке активировать «Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore`3 [RepositoryProject.DomainAuth.ApplicationUser,Microsoft.EntityFrameworkCore.DbContext,System.Guid] '
Как я могу внедрить UserManager в конструктор DbContext, не вызывая ошибок?
Или есть способ создать идентификационного пользователя без использования UserManager, чтобы я мог удалить зависимость из класса SeedData и, следовательно, она мне не понадобится внутри DbContext?
Я постараюсь решить эти проблемы, когда захочу расширить свое приложение. Но на данный момент меня это не беспокоит. @NguyễnKỳDươngTrường
Если вы хотите использовать миграцию для заполнения данных через модель, единственный способ, который я нашел, — это использовать HasData API с пользовательскими данными из базы данных (сначала я создал с помощью usermanager и просто скопировал и вставил). Кроме того, вам нужно настроить модель удостоверения, чтобы добавить UserRoles свойство навигации для добавления роли с помощью HasData... и множество проблем. Я рекомендую вам внимательно прочитать эту страницу из MS Learn.microsoft.com/en-us/ef/core/modeling/data-seeding Мое решение — создать собственную логику заполнения, и я предпочитаю переместить их в отдельное консольное приложение. .
Я уже использую этот метод HasData внутри метода SeedData. Поэтому я попытался использовать старый традиционный способ, добавляя новые строки в таблицу IdentityUser, затем добавляя строки в таблицу IdentityRole, а затем связывая пользователей и роли, добавляя новые строки в таблицу IdentityUserRole. Но я боюсь, что мне не хватает некоторых дополнительных действий, которые выполняются при создании пользователя или при хешировании пароля @NguyễnKỳDươngTrường
Более подробно: если вы включите AccountConfirmed, вам следует установить для EnmailConfirmed значение true. Кстати, UserManager и RoleManager обычно используют Normalized{Object} вместо {Object}. Пожалуйста, установите верхний регистр.
Внедрение службы в конструктор создаст циклическую ссылку. Когда вы определяете свою модель, она не существует. ИМХО, просто добавьте тест запуска в свое приложение; применить миграцию, проверить, пуста ли таблица, заполнить некоторые значения по умолчанию.





Попробуй это:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var userManager = this.GetService<UserManager>();
...
Однако есть одна вещь: я бы сделал это в вашей реализации IdentityDbContext, используя HasData, как предложено в одном из комментариев выше, НЕ в DbContext OnModelCreating, т.е.
public MyIdentityDbContext : IdentityDbContext<ApplicationUser, IdentityRole, string> {
public MyIdentityDbContext(DbContextOptions<MyIdentityDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
SetupData(builder);
}
private void SetupData(ModelBuilder builder)
{
// Users
var defaultUsers = new List<ApplicationUser>() {
new ApplicationUser()
{
Id = "USER_HASH"
Name = "name",
Email = "[email protected]",
NormalizedEmail = "[email protected]",
UserName = "username",
NormalizedUserName = "NormalizedUserName",
UserType = UserType.WebsiteAdmin,
// Use your known hash here (the hash below is for Password123!)
PasswordHash = "PasswordHash",
// Use your hash here
SecurityStamp = "SecurityStamp",
AccessFailedCount = 0,
EmailConfirmed = true,
PhoneNumberConfirmed = true,
TwoFactorEnabled = false
}
};
builder.Entity<ApplicationUser>().HasData(defaultUsers);
// Roles
var defaultRoles = new List<IdentityRole>(){
new IdentityRole { Id = "ROLE_HASH", Name =
RoleNames.WebsiteAdminRole, NormalizedName =
RoleNames.WebsiteAdminRole.ToUpper() }
};
builder.Entity<IdentityRole>().HasData(defaultRoles);
// User roles
var defaultUserRoles = new List<IdentityUserRole<string>>(){
new IdentityUserRole<string>(){
Id = "USER_HASH",
RoleId = "ROLE_HASH"
}
};
builder.Entity<IdentityUserRole<string>>().HasData(defaultUsersRoles);
}
}
Примечание. Хэш, который у меня есть для приведенного выше пароля, соответствует API, настроенному на использование IdentityV3, т.е.
builder.Services.Configure<PasswordHasherOptions>(options => options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV3);
Вы можете создать свой собственный хэш пароля, используя
var passwordHasher = this.GetService<IPasswordHasher<IdentityUser>>();
var passwordHash = passwordHasher.HashPassword(user, "YourPassword");
куда бы я положил эту инструкцию? внутри SeedData конструктора? Я пытался поместить его туда, но для класса GetService не определен метод SeedData?
в OnModelCreating (или в методе вашей реализации DbContext). Я добавил пример использования метода HasData, который, по моему мнению, вам действительно следует делать.
Проблема, с которой вы столкнулись, связана с смешиванием задач определения схемы базы данных и управления пользователями в OnModelCreating. OnModelCreating в первую очередь предназначен для настройки сущностей и отношений с использованием Fluent API и не идеально подходит для операционных задач, таких как создание пользователей. Как вы описали, попытка внедрить UserManager непосредственно в ваш DbContext может привести к проблемам с разрешением зависимостей из-за циклических зависимостей и способа управления областями внедрения зависимостей в приложениях ASP.NET Core. Вместо этого вы можете попытаться заполнить свои данные, используя отдельный механизм заполнения, который запускается после настройки контекста вашей базы данных.
Для этого сначала создайте начальный класс, который не зависит от конструктора DbContext:
public class SeedData
{
private readonly UserManager<ApplicationUser> _userManager;
public SeedData(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task SeedWebsiteAdminsAsync()
{
var superAdminLogin = new ApplicationUser()
{
UserName = "username",
Email = "youremail",
UserType = UserType.WebsiteAdmin
};
var result = await _userManager.CreateAsync(superAdminLogin, "test");
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(superAdminLogin, RoleNames.WebsiteAdminRole);
}
// Additional logic for saving related data to the database
}
public async Task SeedCompanyAdminsAsync()
{
// Your logic to seed company admins
}
}
В своем Program.cs настройте свое приложение для запуска заполнения после запуска приложения и после выполнения миграции. Удалите исходный код из OnModelCreating.
На самом деле, размещение кода логики заполнения внутри
OnModelCreating— это яма падения. В производственных средах, если приложение масштабируется на несколько экземпляров, может возникнуть проблема состояния гонки. Усилия по решению этой проблемы не малы. Вашей текущей проблемы и потенциальных проблем можно избежать, создав еще одно новое консольное приложение, взяв на себя ответственность только за отправку данных.