英文:
Npgsql - "Couldn't set databaseconfig" error when updating database or removing migration
问题
我正在配置一个使用EntityFrameworkCore
和PostgreSQL
作为数据库管理系统的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 "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
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 $"Server={Host},{Port};DatabaseConfig={Name};User Id={User};Password={Password}"; }
}
}
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($"{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()
);
}
}
And this is the context:
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
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 $"Host={Host};Port={Port};Database={Name};Username={User};Password={Password}"; }
}
Now it works.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论