Как заселить пользователей и роли с помощью программы Code First Migration с использованием Identity ASP.NET Core

Я создал новый чистый проект asp.net 5 (rc1-final). Использование Identity Authentication У меня просто есть ApplicationDbContext.cs со следующим кодом:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        // On event model creating
        base.OnModelCreating(builder);
    }
}

Обратите внимание, что ApplicationDbContext использует IdentityDbContext, а не DbContext.

Существует любой IdentityConfig.cs. Где мне нужно поставить классическое защищенное переопределение void Seed для создания роли и пользователя, если он не существует?

Ответы

Ответ 1

Мой способ сделать это - создать класс в пространстве имен моделей.

public class SampleData
{
    public static void Initialize(IServiceProvider serviceProvider)
    {
        var context = serviceProvider.GetService<ApplicationDbContext>();

        string[] roles = new string[] { "Owner", "Administrator", "Manager", "Editor", "Buyer", "Business", "Seller", "Subscriber" };

        foreach (string role in roles)
        {
            var roleStore = new RoleStore<IdentityRole>(context);

            if (!context.Roles.Any(r => r.Name == role))
            {
                roleStore.CreateAsync(new IdentityRole(role));
            }
        }


        var user = new ApplicationUser
        {
            FirstName = "XXXX",
            LastName = "XXXX",
            Email = "[email protected]",
            NormalizedEmail = "[email protected]",
            UserName = "Owner",
            NormalizedUserName = "OWNER",
            PhoneNumber = "+111111111111",
            EmailConfirmed = true,
            PhoneNumberConfirmed = true,
            SecurityStamp = Guid.NewGuid().ToString("D")
        };


        if (!context.Users.Any(u => u.UserName == user.UserName))
        {
            var password = new PasswordHasher<ApplicationUser>();
            var hashed = password.HashPassword(user,"secret");
            user.PasswordHash = hashed;

            var userStore = new UserStore<ApplicationUser>(context);
            var result = userStore.CreateAsync(user);

        }

        AssignRoles(serviceProvider, user.Email, roles);

        context.SaveChangesAsync();
    }

    public static async Task<IdentityResult> AssignRoles(IServiceProvider services, string email, string[] roles)
    {
        UserManager<ApplicationUser> _userManager = services.GetService<UserManager<ApplicationUser>>();
        ApplicationUser user = await _userManager.FindByEmailAsync(email);
        var result = await _userManager.AddToRolesAsync(user, roles);

        return result;
    }

}

Чтобы запустить этот код при запуске. В Startup.cs в конце метода configure сразу после настройки маршрута добавьте следующий код, как сказал ранее Стаффорд Уильямс.

SampleData.Initialize(app.ApplicationServices);

Ответ 2

На момент написания этой статьи для посева базы данных нет плагина, но вы можете создать класс и добавить его в свой контейнер, чтобы сделать то же самое при запуске приложения, вот как я это сделал сначала создайте класс:

public class YourDbContextSeedData
{
    private YourDbContext _context;

    public YourDbContextSeedData(YourDbContext context)
    {
        _context = context;
    }

    public async void SeedAdminUser()
    {
        var user = new ApplicationUser
        {
            UserName = "[email protected]",
            NormalizedUserName = "[email protected]",
            Email = "[email protected]",
            NormalizedEmail = "[email protected]",
            EmailConfirmed = true,
            LockoutEnabled = false,
            SecurityStamp = Guid.NewGuid().ToString()
        };

        var roleStore = new RoleStore<IdentityRole>(_context);

        if (!_context.Roles.Any(r => r.Name == "admin"))
        {
            await roleStore.CreateAsync(new IdentityRole { Name = "admin", NormalizedName = "admin" });
        }

        if (!_context.Users.Any(u => u.UserName == user.UserName))
        {
            var password = new PasswordHasher<ApplicationUser>();
            var hashed = password.HashPassword(user, "password");
            user.PasswordHash = hashed;
            var userStore = new UserStore<ApplicationUser>(_context);
            await userStore.CreateAsync(user);
            await userStore.AddToRoleAsync(user, "admin");
        }

        await _context.SaveChangesAsync();
    }

Зарегистрируйте тип в методе ConfigureServices вашего класса Startup.cs:

services.AddTransient<YourDbContextSeedData>();

Затем передайте класс YourDbContextSeedData методу Configure вашего класса Startup.cs и используйте его:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, YourDbContextSeedData seeder)
{
  seeder.SeedAdminUser();
}

Ответ 3

Это еще не реализовано. Работая вокруг, просто напишите свой собственный класс, который будет проверять базу данных на наличие сущностей, добавлять их, если они не существуют, и вызвать этот класс из Startup.cs.

Ответ 4

Если у вас есть проблемы с асинхронностью, попробуйте следующий код:

    protected override void Seed(ApplicationDbContext context)
    {
        //  This method will be called after migrating to the latest version.

        string[] roles = new string[] { "Admin", "User" };
        foreach (string role in roles)
        {
            if (!context.Roles.Any(r => r.Name == role))
            {
                context.Roles.Add(new IdentityRole(role));
            }
        }

        //create user UserName:Owner Role:Admin
        if (!context.Users.Any(u => u.UserName == "Owner"))
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
            var user = new ApplicationUser
            {
                FirstName = "XXXX",
                LastName = "XXXX",
                Email = "[email protected]",
                UserName = "Owner",
                PhoneNumber = "+111111111111",
                EmailConfirmed = true,
                PhoneNumberConfirmed = true,
                SecurityStamp = Guid.NewGuid().ToString("D"),
                PasswordHash = userManager.PasswordHasher.HashPassword("secret"),
                LockoutEnabled = true,
            };
            userManager.Create(user);
            userManager.AddToRole(user.Id, "Admin");
        }            

        context.SaveChanges();
    }

Ответ 5

Добавьте следующий класс в пространство имен моделей. Он работает для добавления нескольких пользователей и ролей, а также добавляет роли существующим пользователям (например, учетные записи факеров). Назовите его так app.SeedUsersAndRoles(); из startup.cs

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;

namespace MyApplication.Models
{
    public static class DataSeeder
    {
        public static async void SeedUsersAndRoles(this IApplicationBuilder app)
        {
            var context = app.ApplicationServices.GetService<ApplicationDbContext>();
            UserWithRoles[] usersWithRoles = {
                new UserWithRoles("Admin", new string[] { "Administrator" , "Distributor" },"somepassword"),//user and optional roles and password you want to seed 
                new UserWithRoles("PlainUser"),
                new UserWithRoles("Jojo",new string[]{"Distributor" }) //seed roles to existing users (e.g. facebook login).
            };

            foreach (var userWithRoles in usersWithRoles)
            {
                foreach (string role in userWithRoles.Roles)
                    if (!context.Roles.Any(r => r.Name == role))
                    {
                        var roleStore = new RoleStore<IdentityRole>(context);
                        await roleStore.CreateAsync(new IdentityRole(role));
                    }
                var ExistingUser = context.Users.FirstOrDefault(p => p.NormalizedUserName == userWithRoles.User.NormalizedUserName);
                if (ExistingUser == null) //the following syntax: !context.Users.FirstOrDefault(p => p.NormalizedUserName == userWithRoles.User.NormalizedUserName)) 
                                            //provokes execption:(ExecuteReader requires an open and available Connection.) 
                    await new UserStore<ApplicationUser>(context).CreateAsync(userWithRoles.User);
                await app.AssignRoles(userWithRoles); //assign also to existing users.
            }

            context.SaveChangesAsync();
        }

        public static async Task<IdentityResult> AssignRoles(this IApplicationBuilder app, UserWithRoles uWR)
        {
            UserManager<ApplicationUser> _userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
            ApplicationUser user = await _userManager.FindByNameAsync(uWR.User.NormalizedUserName);
            var result = await _userManager.AddToRolesAsync(user, uWR.Roles);
            return result;
        }
    }
    public class UserWithRoles
    {
        private ApplicationUser user;
        public ApplicationUser User { get { return user; } }
        public string[] Roles { get; set; }
        public UserWithRoles(string name, string[] roles = null, string password = "secret")
        {
            if (roles != null)
                Roles = roles;
            else
                Roles = new string[] { };
            user = new ApplicationUser
            {
                Email = name + "@gmail.com", NormalizedEmail = name.ToUpper() + "@GMAIL.COM",
                UserName = name, NormalizedUserName = name.ToUpper(),
                PhoneNumber = "+1312341234",
                EmailConfirmed = true,
                PhoneNumberConfirmed = true,
                SecurityStamp = Guid.NewGuid().ToString("D"),
            };
            user.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(user, password);
        }
    }
}

Ответ 6

Следующая строка создает запись в таблице AspNetRoles, но не заполняет столбец NormalizedName.

Замените для этого столбца следующее:

RoleManager<IdentityRole> roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
roleManager.CreateAsync(new IdentityRole(role));

Ответ 7

Так что это решение, основанное на ответе Мухаммеда Абдуллы. Включено несколько улучшений кода, улучшена читаемость кода и он работает с .net core 2.

 public class Seed
    {
        public static async Task Initialize(IServiceProvider serviceProvider, IConfiguration configuration)
        {
            var usrName = configuration.GetSection("Admin").GetSection("UserName").Value;
            var email = configuration.GetSection("Admin").GetSection("Email").Value;
            var pass = configuration.GetSection("Admin").GetSection("Pass").Value;
            var roles = new string[4] { OWNER, ADMIN, SENIOR, USER };

            if(await CreateUser(serviceProvider, email, usrName, pass, roles))
            {
                await AddToRoles(serviceProvider, email, roles);
            }
        }

        private static async Task<bool> CreateUser(IServiceProvider serviceProvider, string email, string usrName, string pass, string[] roles)
        {
            var res = false;

            using (var scope = serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetService<BaseContext>();

                if (!context.ApplicationUsers.Any(u => u.NormalizedUserName == usrName.ToUpper()))
                {
                    var roleStore = scope.ServiceProvider.GetService<RoleManager<IdentityRole>>();

                    foreach (string role in roles)
                    {
                        if (!context.Roles.Any(r => r.Name == role))
                        {
                            await roleStore.CreateAsync(new IdentityRole(role)).ConfigureAwait(false);
                        }
                    }

                    var user = new ApplicationUser
                    {
                        UserName = usrName,
                        Email = email,
                        EmailConfirmed = true,
                        NormalizedEmail = email.ToUpper(),
                        NormalizedUserName = usrName.ToUpper(),
                        PhoneNumber = null,
                        PhoneNumberConfirmed = true,
                        SecurityStamp = Guid.NewGuid().ToString()
                    };

                    var password = new PasswordHasher<ApplicationUser>();
                    user.PasswordHash = password.HashPassword(user, pass); ;

                    var userStore = new UserStore<ApplicationUser>(context);
                    res = (await userStore.CreateAsync(user).ConfigureAwait(false)).Succeeded;
                }

                return res;
            }
        }

        private static async Task AddToRoles(IServiceProvider serviceProvider, string email, string[] roles)
        {
            using (var scope = serviceProvider.CreateScope())
            {
                var userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
                var usr = await userManager.FindByEmailAsync(email).ConfigureAwait(false);
                await userManager.AddToRolesAsync(usr, roles).ConfigureAwait(false);
            }           
        }
    }