1

In my website, I want to let login the admin with an account in order to modify the database. Also, I might add some roles and accounts in the future.

So, I am trying to implement identity.

I made a project in Visual Studio 2019 (not sure if that is important) and choose ASP.NET Core 2.2 (I installed Core 2.2 on my own) with MVC, Authentication with individual user accounts and I let clicked the box which said: "configure for HTTPS".

Once made the project, I open it, make a random account, got an error and then migrate the database to fix it.

Then, I add in appsetting.json:

"UserSettings": {
    "UserName": "MyAdminUser",
    "UserEmail": "MyAdminUser@gmail.com",
    "UserPassword": "A_123456a"
}

Create a new class inside the folder Data:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace WebApplication1.Data {
    public static class Seed {
        public static async Task CreateRoles(IServiceProvider serviceProvider, IConfiguration Configuration) {
            // Add customs roles
            var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
            // Note: I used IdentityUser instead of make my ApplicationUser as other people do because the default idendity is fine for me, I don't need additional fields nor I want to make this more difficult.
            var UserManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();
            string[] roleNames = { "Admin" };
            IdentityResult roleResult;

            foreach (var roleName in roleNames) {
                // Create roles and seeding them to the database
                var roleExist = await RoleManager.RoleExistsAsync(roleName);
                if (!roleExist) {
                    roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
                }
            }

            // Create a super user
            var poweruser = new IdentityUser {
                UserName = Configuration.GetSection("AppSettings")["UserSettings:UserName"],
                Email = Configuration.GetSection("AppSettings")["UserSettings:UserEmail"]
            };

            string userPassword = Configuration.GetSection("AppSettings")["UserSettings:UserPassword"];
            var user = await UserManager.FindByEmailAsync(Configuration.GetSection("AppSettings")["UserSettings:UserEmail"]);

            if (user == null) {
                var createPowerUser = await UserManager.CreateAsync(poweruser, userPassword);
                if (createPowerUser.Succeeded) {
                    // Assign the new user the "Admin" role 
                    await UserManager.AddToRoleAsync(poweruser, "Admin");
                }
            }
        }
    }
}

And finally replace the Main method of Program class to:

public static void Main(string[] args) {
    var host = CreateWebHostBuilder(args).Build();    
    using (var scope = host.Services.CreateScope()) {
        var services = scope.ServiceProvider;
        var serviceProvider = services.GetRequiredService<IServiceProvider>();
        var configuration = services.GetRequiredService<IConfiguration>();
        Seed.CreateRoles(serviceProvider, configuration).Wait();
    }    
    host.Run();
}

But when I run it, the error HTTP Error 500.30 - ANCM In-Process Start Failure is raised on the webpage and the debug console said the following exceptions:

  • 'System.InvalidOperationException' in Microsoft.Extensions.DependencyInjection.Abstractions.dll
  • 'System.AggregateException' in System.Private.CoreLib.dll

I don't know how to fix that.

Also, I have found all these questions (with their answers) 1, 2, 3, 4, 5, 6 and these Non-SO 7 and 8. But my main problem with them is that each one use a different way to implement an Identity (some of them obsolete to 2.2) and I don't know which one is better, even some raise error on my project or I don't understand at all, so I tried to do this (I read that execute this code in Program is better than Startup in perfomance).

By the way, I also tried to remove my code from Programm and instead add in the Configure method from Startup the parameter IServiceProvider serviceProvider and:

// I used _ = because VS said me something about async
_ = Seed.CreateRoles(serviceProvider, Configuration);
Ender Look
  • 1,954
  • 2
  • 16
  • 32

2 Answers2

1

The issue is that you're attempting to retrieve scoped services from IServiceProvider without creating a scope. Interestingly, you created a scope around your call to Seed.CreateRoles, but then you pass in IServiceProvider, which is not scoped.

What you should be doing is either passing in IServiceProvider and creating the scope inside your Seed.CreateRoles method, or leave the scope creation where it is now and instead pass in your scoped services, i.e. RoleManager and UserManager.

Edit

You need to do one of the following:

  1. Inject IServiceProvider and then create your scope inside the seed method:

    var host = CreateWebHostBuilder(args).Build();
    Seed.CreateRoles(host.Services);
    host.Run();
    

    Then:

    public static async Task CreateRoles(IServiceProvider services) {
        var configuration = services.GetRequiredService<IConfiguration>();
        using (var scope = services.CreateScope())
        {
            var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
            var userManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
    
            // seed data
        }
    }
    
  2. Inject your scoped services:

    var host = CreateWebHostBuilder(args).Build();
    
    var configuration = host.Services.GetRequiredService<IConfiguration>();
    using (var scope = host.Services.CreateScope())
    {
        var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
        Seed.CreateRoles(configuration, roleManager, userManager);
    }
    host.Run();
    
Chris Pratt
  • 207,690
  • 31
  • 326
  • 382
  • I'm sorry but I think I've not understood you. So, should I pass as a parameter `services` variable and then, inside `Seed.CreateRoles` has as only parameter `IServiceProvider serviceProvider` and do `var Configuration = serviceProvider.GetRequiredService();`? That doesn't fix the error. I found that about scopes on the internet and thought it was correct. – Ender Look Feb 01 '19 at 16:44
  • See edit above. Essentially, working with your scoped services have to be done within the scope created by the same `IServiceProvider` instance. If doesn't matter if you're calling your seed method inside a scope, if you're still using `IServiceProvider` directly inside. `IServiceProvider` is not scoped, so you cannot pull scoped services out of it without creating yet another scope. – Chris Pratt Feb 01 '19 at 18:17
0

Running your code shows an exception on the following line:

var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();

Based on the exception it appears you are missing the configuration in the startup.cs file to enable Identity. I can't see your startup.cs but I suspect you are missing the whole services.AddIdentity(...) calls.

I suggest you take some time to read up on how to configure Identity in ASP.NET Core. The Microsoft Docs are always a good place to start.

Also, the second question you mentioned in your question has good steps. You specifically need to look at Step 2 and Step 3.

Simply Ged
  • 6,639
  • 9
  • 29
  • 37
  • I don't have `services.AddIdentity(...)`, but, Visual Studio generated this code `services.AddDefaultIdentity().AddDefaultUI(UIFramework.Bootstrap4).AddEntityFrameworkStores();`, isn't that a configuration of Identity?. I replaced that with `services.AddIdentity().AddEntityFrameworkStores().AddDefaultTokenProviders();` but I got `'System.ArgumentNullException' in System.Private.CoreLib.dll` and `'System.AggregateException' in System.Private.CoreLib.dll`. I'll try read again the second question more carefully. – Ender Look Feb 01 '19 at 01:50