IdentityServer4 数据库迁移问题

IdentityServer4 database migration issue

我正在使用 .net core 3.1 构建 identityserver4。我正在使用数据库迁移来构建数据库。我遇到的问题是播种方法。我的播种方法如下:

private void ClientSeed(ModelBuilder builder)
        {
            builder.Entity<ApiResource>()
               .HasData(
                   new ApiResource
                   {
                       Name = "PatientPortalAPI",
                       DisplayName = "Patient Portal API",

                       ApiSecrets =
                       {
                           new Secret("secret".Sha256())
                       },

                       Scopes =
                       {
                           new Scope()
                           {
                                 Name = StandardScopes.OfflineAccess,
                                 DisplayName = StandardScopes.OfflineAccess,
                                 Description = null,
                                 Required = false,
                                 Emphasize = false,
                                 ShowInDiscoveryDocument = true,
                           },
                           new Scope()
                           {
                                 Name = StandardScopes.OpenId,
                                 DisplayName = StandardScopes.OpenId,
                                 Description = null,
                                 Required = false,
                                 Emphasize = false,
                                 ShowInDiscoveryDocument = true,
                           }
                       }
                   }
               );

            builder.Entity<IdentityResource>().HasData(
                    new IdentityResource()
                    {
                        Enabled = true,
                        Name = "openid",
                        DisplayName = "Your user identifier",
                        Description = null,
                        Required = true,
                        Emphasize = false,
                        ShowInDiscoveryDocument = true,
                    },
                    new IdentityResource()
                    {
                        Enabled = true,
                        Name = "profile",
                        DisplayName = "User profile",
                        Description = "Your user profile information (first name, last name, etc.)",
                        Required = false,
                        Emphasize = true,
                        ShowInDiscoveryDocument = true,
                    });

            builder.Entity<Client>()
                .HasData(
                    new Client
                    {
                        ClientId = "t8agr5xKt4",
                        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                        ClientSecrets =
                        {
                            new Secret("secret".Sha256())
                        },

                        AllowedScopes =
                        {
                            StandardScopes.OpenId,
                            StandardScopes.Profile,
                            StandardScopes.Email,
                            StandardScopes.Address,
                            "api1",
                            StandardScopes.OfflineAccess
                        },

                        AllowOfflineAccess = true,
                        RefreshTokenUsage = TokenUsage.ReUse,
                        RefreshTokenExpiration = TokenExpiration.Sliding,
                    });
        }

我在添加迁移时遇到的错误是这样的:

System.InvalidOperationException: No suitable constructor found for entity type 'Claim'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'reader' in 'Claim(BinaryReader reader)'; cannot bind 'reader', 'subject' in 'Claim(BinaryReader reader, ClaimsIdentity subject)'; cannot bind 'type', 'value' in 'Claim(string type, string value)'; cannot bind 'type', 'value', 'valueType' in 'Claim(string type, string value, string valueType)'; cannot bind 'type', 'value', 'valueType', 'issuer' in 'Claim(string type, string value, string valueType, string issuer)'; cannot bind 'type', 'value', 'valueType', 'issuer', 'originalIssuer' in 'Claim(string type, string value, string valueType, string issuer, string originalIssuer)'; cannot bind 'type', 'value', 'valueType', 'issuer', 'originalIssuer', 'subject' in 'Claim(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject)'; cannot bind 'type', 'value', 'valueType', 'issuer', 'originalIssuer', 'subject', 'propertyKey', 'propertyValue' in 'Claim(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject, string propertyKey, string propertyValue)'; cannot bind 'other' in 'Claim(Claim other)'; cannot bind 'other', 'subject' in 'Claim(Claim other, ClaimsIdentity subject)'.
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.ConstructorBindingConvention.ProcessModelFinalized(IConventionModelBuilder modelBuilder, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
   at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
No suitable constructor found for entity type 'Claim'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'reader' in 'Claim(BinaryReader reader)'; cannot bind 'reader', 'subject' in 'Claim(BinaryReader reader, ClaimsIdentity subject)'; cannot bind 'type', 'value' in 'Claim(string type, string value)'; cannot bind 'type', 'value', 'valueType' in 'Claim(string type, string value, string valueType)'; cannot bind 'type', 'value', 'valueType', 'issuer' in 'Claim(string type, string value, string valueType, string issuer)'; cannot bind 'type', 'value', 'valueType', 'issuer', 'originalIssuer' in 'Claim(string type, string value, string valueType, string issuer, string originalIssuer)'; cannot bind 'type', 'value', 'valueType', 'issuer', 'originalIssuer', 'subject' in 'Claim(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject)'; cannot bind 'type', 'value', 'valueType', 'issuer', 'originalIssuer', 'subject', 'propertyKey', 'propertyValue' in 'Claim(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject, string propertyKey, string propertyValue)'; cannot bind 'other' in 'Claim(Claim other)'; cannot bind 'other', 'subject' in 'Claim(Claim other, ClaimsIdentity subject)'.

排除上述播种方法,数据库迁移非常有效。一旦我在 ConfigurationDbContext class 的 OnModelCreating 方法中调用播种方法,它就会因上述错误而失败。

谁能看出我哪里出错了???

期待您的回复。

提前致谢,

萨姆

你应该在 IdentityServer4.EntityFramework.Entities 上使用类型,你在代码中混合了模型和实体,尝试删除 using IdentityServer4.Models; 以清楚地了解什么是实体,什么是模型。正确使用实体后,您会看到例如 ApiResource 没有像 ApiSecrets 那样的 属性,它是 Secrets.

还有另一种方法可以通过添加方法 ob startup.cs 来初始化数据库,例如:

private void InitializeDatabase(IApplicationBuilder app)
{
    using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
    {
        serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();

        var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
        context.Database.Migrate();
        if (!context.Clients.Any())
        {
            foreach (var client in Config.Clients)
            {
                context.Clients.Add(client.ToEntity());
            }
            context.SaveChanges();
        }

        if (!context.IdentityResources.Any())
        {
            foreach (var resource in Config.Ids)
            {
                context.IdentityResources.Add(resource.ToEntity());
            }
            context.SaveChanges();
        }

        if (!context.ApiResources.Any())
        {
            foreach (var resource in Config.Apis)
            {
                context.ApiResources.Add(resource.ToEntity());
            }
            context.SaveChanges();
        }
    }
}

然后这样称呼它;

public void Configure(IApplicationBuilder app)
{
    // this will do the initial DB population
    InitializeDatabase(app);

    // the rest of the code that was already here
    // ...
}

阅读更多here