Npgsql – "Couldn't set databaseconfig" error when updating database or removing migration

huangapple go评论79阅读模式
英文:

Npgsql - "Couldn't set databaseconfig" error when updating database or removing migration

问题

我正在配置一个使用EntityFrameworkCorePostgreSQL作为数据库管理系统的dotnet项目。因此,我必须使用Npgsql

数据库正在Docker容器中运行,并使用以下脚本初始化:

#!/bin/bash

psql --username "postgres" --dbname "postgres" <<EOSQL

SELECT 'CREATE DATABASE $Name' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$Name;);\gexec

\connect "$Name";

DO $$
BEGIN
    IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '$User') THEN
        RAISE NOTICE 'Creating user $User';
        CREATE ROLE "$User" WITH PASSWORD '$Password';
    ELSE
        RAISE NOTICE 'Updating password for user $User';
        ALTER ROLE "$User" WITH PASSWORD '$Password';
    END IF;
END
$$;

ALTER ROLE "$User" WITH LOGIN;
GRANT ALL ON DATABASE "$Name" TO "$User";

CREATE SCHEMA IF NOT EXISTS "$Schema" AUTHORIZATION "$User";

CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
CREATE EXTENSION IF NOT EXISTS "pgstattuple";
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
EOSQL

环境变量从appsettings.json文件中读取并使用其他脚本加载。这似乎有效,因为已创建数据库和用户,我可以连接到数据库、模式和创建表。

在dotnet项目中,我在一个简单的类中读取数据库配置:

public class DatabaseConfig
{
  public string Host { get; set; }
  public int Port { get; set; }
  public string Schema { get; set; }
  public string Name { get; set; }
  public string User { get; set; }
  public string Password { get; set; }

  public string ConnectionString
  {
    get { return $"Server={Host},{Port};Database={Name};User Id={User};Password={Password}"; }
  }
}

并且在启动时配置数据库如下:

public class Startup
{
  public static void Configure(ConfigurationManager configuration, IServiceCollection services)
  {
    IConfiguration appSettings = new ConfigurationBuilder()
      .AddJsonFile($"{Environment.CurrentDirectory}/appsettings.json")
      .AddEnvironmentVariables()
      .Build();

    ConfigureInjector(configuration, appSettings, services);
    ConfigureDatabase(appSettings, services);
  }

  private static void ConfigureInjector(
    ConfigurationManager configuration,
    IConfiguration appSettings,
    IServiceCollection services
  )
  {
    services.AddSingleton<DatabaseConfig>(
      appSettings.GetRequiredSection("Database").Get<DatabaseConfig>()
    );
  }

  private static void ConfigureDatabase(IConfiguration configuration, IServiceCollection services)
  {
    DatabaseConfig databaseConfig = configuration
      .GetRequiredSection("Database")
      .Get<DatabaseConfig>();

    services.AddDbContext<MyContext>(
      options => options.UseNpgsql(databaseConfig.ConnectionString).UseSnakeCaseNamingConvention()
    );
  }
}

这是上下文:

public class MyContext : DbContext
{
  private readonly DatabaseConfig databaseConfig;

  public DbSet<Brand> Brands { get; set; }

  public MyContext(DbContextOptions<MyContext> options, DatabaseConfig databaseConfig)
    : base(options) => this.databaseConfig = databaseConfig;

  protected override void OnConfiguring(DbContextOptionsBuilder options)
  {
    options.UseNpgsql(
      databaseConfig.ConnectionString,
      x => x.MigrationsHistoryTable("migration", databaseConfig.Schema)
    );
  }

  protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
    modelBuilder.HasDefaultSchema(databaseConfig.Schema);
  }
}

使用ef add运行成功,迁移被正确创建。但是,当我尝试更新数据库或移除迁移时,出现以下错误:

System.ArgumentException: 无法设置databaseconfig (参数'databaseconfig')
System.Collections.Generic.KeyNotFoundException: 字典中没有找到给定的键。
在Npgsql.NpgsqlConnectionStringBuilder.GeneratedSetter(String关键字,Object值)
在Npgsql.NpgsqlConnectionStringBuilder.set_Item(String关键字,Object值)
内部异常堆栈跟踪结束
在Npgsql.NpgsqlConnectionStringBuilder.set_Item(String关键字,Object值)
在System.Data.Common.DbConnectionStringBuilder.set_ConnectionString(String值)
在Npgsql.NpgsqlConnectionStringBuilder..ctor(String连接字符串)
在Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlDatabaseCreator.Exists(Boolean async,CancellationToken cancellationToken)
在Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlDatabaseCreator.Exists()
在Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists()
在Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
在Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration,String connectionString,String contextType)
在Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration,String connectionString,String contextType)
在Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
在Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

英文:

I am configuring a dotnet project with EntityFrameworkCore and PostgreSQL as DBMS. So I have to use Npgsql.

The database is running in a docker container and it has been initialized with this script:

#!/bin/bash

psql --username &quot;postgres&quot; --dbname &quot;postgres&quot; &lt;&lt;EOSQL

SELECT &#39;CREATE DATABASE $Name&#39; WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = &#39;$Name&#39;);\gexec

\connect &quot;$Name&quot;;

DO $$
BEGIN
    IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE  rolname = &#39;$User&#39;) THEN
        RAISE NOTICE &#39;Creating user $User&#39;;
        CREATE ROLE &quot;$User&quot; WITH PASSWORD &#39;$Password&#39;;
    ELSE
        RAISE NOTICE &#39;Updating password for user $User&#39;;
        ALTER ROLE &quot;$User&quot; WITH PASSWORD &#39;$Password&#39;;
    END IF;
END
$$;

ALTER ROLE &quot;$User&quot; WITH LOGIN;
GRANT ALL ON DATABASE &quot;$Name&quot; TO &quot;$User&quot;;

CREATE SCHEMA IF NOT EXISTS &quot;$Schema&quot; AUTHORIZATION &quot;$User&quot;;

CREATE EXTENSION IF NOT EXISTS &quot;pg_stat_statements&quot;;
CREATE EXTENSION IF NOT EXISTS &quot;pgstattuple&quot;;
CREATE EXTENSION IF NOT EXISTS &quot;uuid-ossp&quot;;
EOSQL

The environment variables are read from the appsettings.json file and loaded with other script. This it seems working because database and user are created, I can connect to the database, schema and create tables.

In the dotnet project I am reading the database config in a simple class:

public class DatabaseConfig
{
  public string Host { get; set; }
  public int Port { get; set; }
  public string Schema { get; set; }
  public string Name { get; set; }
  public string User { get; set; }
  public string Password { get; set; }

  public string ConnectionString
  {
    get { return $&quot;Server={Host},{Port};DatabaseConfig={Name};User Id={User};Password={Password}&quot;; }
  }
}

And this is how the database is configured at startup:

public class Startup
{
  public static void Configure(ConfigurationManager configuration, IServiceCollection services)
  {
    IConfiguration appSettings = new ConfigurationBuilder()
      .AddJsonFile($&quot;{Environment.CurrentDirectory}/appsettings.json&quot;)
      .AddEnvironmentVariables()
      .Build();

    ConfigureInjector(configuration, appSettings, services);
    ConfigureDatabase(appSettings, services);
  }

  private static void ConfigureInjector(
    ConfigurationManager configuration,
    IConfiguration appSettings,
    IServiceCollection services
  )
  {
    services.AddSingleton&lt;DatabaseConfig&gt;(
      appSettings.GetRequiredSection(&quot;Database&quot;).Get&lt;DatabaseConfig&gt;()
    );
  }

  private static void ConfigureDatabase(IConfiguration configuration, IServiceCollection services)
  {
    DatabaseConfig databaseConfig = configuration
      .GetRequiredSection(&quot;Database&quot;)
      .Get&lt;DatabaseConfig&gt;();

    services.AddDbContext&lt;MyContext&gt;(
      options =&gt; options.UseNpgsql(databaseConfig.ConnectionString).UseSnakeCaseNamingConvention()
    );
  }
}

And this is the context:

public class MyContext : DbContext
{
  private readonly DatabaseConfig databaseConfig;

  public DbSet&lt;Brand&gt; Brands { get; set; }

  public MyContext(DbContextOptions&lt;MyContext&gt; options, DatabaseConfig databaseConfig)
    : base(options) =&gt; this.databaseConfig = databaseConfig;

  protected override void OnConfiguring(DbContextOptionsBuilder options)
  {
    options.UseNpgsql(
      databaseConfig.ConnectionString,
      x =&gt; x.MigrationsHistoryTable(&quot;migration&quot;, databaseConfig.Schema)
    );
  }

  protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
    modelBuilder.HasDefaultSchema(databaseConfig.Schema);
  }
}

ef add runs successfully, the migration is correctly created. But when I try to update the database or remove the migration I get this error:
> System.ArgumentException: Couldn't set databaseconfig (Parameter 'databaseconfig')
---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at Npgsql.NpgsqlConnectionStringBuilder.GeneratedSetter(String keyword, Object value)
at Npgsql.NpgsqlConnectionStringBuilder.set_Item(String keyword, Object value)
--- End of inner exception stack trace ---
at Npgsql.NpgsqlConnectionStringBuilder.set_Item(String keyword, Object value)
at System.Data.Common.DbConnectionStringBuilder.set_ConnectionString(String value)
at Npgsql.NpgsqlConnectionStringBuilder..ctor(String connectionString)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlDatabaseCreator.Exists(Boolean async, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlDatabaseCreator.Exists()
at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists()
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String connectionString, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String connectionString, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

答案1

得分: 1

已找到问题。我之前使用的连接字符串适用于SQL Server。

更改为

  public string ConnectionString
  {
    get { return $"Host={Host};Port={Port};Database={Name};Username={User};Password={Password}"; }
  }

现在它正常工作。

英文:

I found the problem. The connection string I was using is meant for SQL Server.

Changed with

  public string ConnectionString
  {
    get { return $&quot;Host={Host};Port={Port};Database={Name};Username={User};Password={Password}&quot;; }
  }

Now it works.

huangapple
  • 本文由 发表于 2023年3月7日 17:45:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/75660281.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定