繁体   English   中英

.NET Core EF + 身份与 PostgreSQL

[英].NET Core EF + Identity with PostgreSQL

所以我必须使用我们所有的 .net 核心应用程序从 SQL Server 迁移到 PostgreSQL - 数据库本身及其连接工作正常,但是如何使用数据库上下文以及 .NET Identity 扩展使用的异步方法存在问题。 我将使用我们的数据库播种程序类来解释我的问题。

目前我们已经在 Startup.cs 的 ConfigureServices 方法中定义了这样的 dbContext 和 Identity:

services.AddDbContext < ApplicationDbContext > (options =>
    options.UseNpgsql(
        Configuration.GetConnectionString("DefaultConnection")));

//values seeder
services.AddScoped < Seeder > ();

//http context
services.AddHttpContextAccessor();

//Auth
services.AddDefaultIdentity < User > (options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles < IdentityRole > ()
    .AddEntityFrameworkStores < ApplicationDbContext > ();
services.Configure < IdentityOptions > (o =>
{
    o.Password.RequireDigit = true;
    o.Password.RequireLowercase = true;
    o.Password.RequireNonAlphanumeric = true;
    o.Password.RequireUppercase = true;
    o.Password.RequiredLength = 6;
    o.Password.RequiredUniqueChars = 1;
    o.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    o.Lockout.MaxFailedAccessAttempts = 5;
    o.Lockout.AllowedForNewUsers = true;
    o.User.RequireUniqueEmail = true;
});

和我们的 Seeder.cs:

using SomeApp.Data;
using SomeApp.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;

namespace SomeApp
{
    public class Seeder
    {
        private readonly ApplicationDbContext _dbContext;
        private readonly UserManager<User> _userManager;

        public Seeder(ApplicationDbContext dbContext, UserManager<User> userManager)
        {
            _dbContext = dbContext;
            _userManager = userManager;
        }

        public async Task Seed()
        {
            var roles = new[] { "Superadmin", "User" };
            
            foreach (var role in roles)
            {
                if (_dbContext.Roles.Any(r => r.Name == role))
                    continue;
                
                var roleStore = new RoleStore<IdentityRole>(_dbContext);
            
                var r = new IdentityRole(role)
                {
                    NormalizedName = role.ToUpperInvariant()
                };
                await roleStore.CreateAsync(r);
                await _dbContext.SaveChangesAsync();
            }
            
            if (_userManager.FindByNameAsync("admin1234").Result is null)
                await CreateDefaultUser();
        }

        private async Task CreateDefaultUser()
        {
            var user = new User
            {
                Email = "admin@admin.pl",
                FirstName = "Admin",
                LastName = "Admin",
                UserName = "admin1234",
                Department = "IT",
                ImagePath = "/assets/user_icon.png",
                IsAnonimised = false,
                IsLoggedIn = true
            };

            var result = _userManager.CreateAsync(user, "Test1234_").Result;
            if (result.Succeeded)
            {
                await _userManager.AddToRoleAsync(user, "Superadmin");
            }
        }
    }
}

它不是最漂亮的(这基本上是我们用来学习 .net 核心的项目),但它可以与 SQL Server 一起使用。 情况是它不再与 PostgreSQL 一起使用,因为它会引发如下异常:

Npgsql.NpgsqlOperationInProgressException (0x80004005): The connection is already in state 'Executing'
   at Npgsql.Internal.NpgsqlConnector.<StartUserAction>g__DoStartUserAction|255_0(<>c__DisplayClass255_0& )
   at Npgsql.Internal.NpgsqlConnector.StartUserAction(ConnectorState newState, NpgsqlCommand command, CancellationToken cancellationToken, Boolean attemptPgCancellation)
   at Npgsql.Internal.NpgsqlConnector.StartUserAction(CancellationToken cancellationToken, Boolean attemptPgCancellation)
   at Npgsql.Internal.NpgsqlConnector.Reset(Boolean async)
   at Npgsql.NpgsqlConnection.CloseAsync(Boolean async)
   at Npgsql.NpgsqlConnection.Close()
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.CloseDbConnection()
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Close()
   at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.ClearTransaction()
   at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.Dispose()
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.ResetState(Boolean disposeDbConnection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Dispose()
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.Dispose()
   at Microsoft.EntityFrameworkCore.DbContext.Dispose()
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.Dispose()
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at AllegroSerwis.Program.Main(String[] args) in D:\Work\ASPdotNETProjects\SomeApp\SomeApp\Program.cs:line 10

看起来由于某种原因 await 没有暂停循环的其余部分,直到异步方法完成它的工作并且 PostgreSQL 无法处理一个数据库上下文上的多个调用。 我做了一些挖掘,最普遍的意见是我应该使用工厂模式在每次调用时创建单独的实例,而不是只在各处注入一个上下文。 所以我做了:

//db
services.AddDbContextFactory < ApplicationDbContext > (options =>
    options.UseNpgsql(
        Configuration.GetConnectionString("DefaultConnection")));

并注入工厂而不是上下文,它适用于角色播种循环,我可以在其中创建新的数据库上下文,但不适用于下面使用的 _userManager - 因为它可能从 Startup.cs 获取数据库上下文,并且目前没有在那里定义。 有人可以或多或少地向我解释解决这个问题的最佳实践是什么,并一起使用实体框架 + 身份 + PostgreSQL,最终如何为所有这些实现工厂模式。 在这一点上,我感到非常沮丧,而且 Microsoft 文档缺少任何不是 SQL Server 的东西。

编辑:添加 ApplicationDbContext.cs

public class ApplicationDbContext: IdentityDbContext
{
    public DbSet < User > Users
    {
        get;
        set;
    }
    public DbSet < IdentityRole > Roles
    {
        get;
        set;
    }
    public DbSet < Summary > Summaries
    {
        get;
        set;
    }
    public DbSet < Report > Reports
    {
        get;
        set;
    }

    //report models subsets
    public DbSet < LogginData > LogginData
    {
        get;
        set;
    }
    public DbSet < EnergyCapsules > EnergyCapsules
    {
        get;
        set;
    }
    public DbSet < BossFightLog > BossFight
    {
        get;
        set;
    }
    public DbSet < Interaction > Interactions
    {
        get;
        set;
    }
    public DbSet < CustomerCall > Calls
    {
        get;
        set;
    }
    public DbSet < Questionaire > Questionaires
    {
        get;
        set;
    }
    public DbSet < Post > Posts
    {
        get;
        set;
    }
    public DbSet < FaqItem > FaqItems
    {
        get;
        set;
    }

    public ApplicationDbContext(DbContextOptions < ApplicationDbContext > options): base(options)
    {}

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.HasDefaultSchema("public");
        base.OnModelCreating(builder);
        builder.Entity < User > ()
            .Property(u => u.Email)
            .IsRequired();
        builder.Entity < Role > ()
            .Property(r => r.Name)
            .IsRequired();
    }
}

  • 你不需要await _dbContext.SaveChangesAsync(); 因为
    RoleStore.CreateAsync()已经在做这个操作了。

  • 除非必须,否则不要在 for 循环中使用数据库访问。 为此创建列表并使用_dbSet.AddRange()重载。

  • 确保等待异步数据库调用。

通过应用上述最佳实践,我做了与您相同的事情,并且它有效。 查看我的模板以获取示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM