I am doing a some training with .Net 6.0, C# and Entity Framework Core. I got to the point that EF can generate a new migration but hen I run dotnet ef database update
I am getting an error:
Failed executing DbCommand (38ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
IF SCHEMA_ID(N'{schema}') IS NULL EXEC(N'CREATE SCHEMA [{schema}];');
CREATE TABLE [{schema}].[Migrations] (
[MigrationId] nvarchar(150) NOT NULL,
[ProductVersion] nvarchar(32) NOT NULL,
CONSTRAINT [PK_Migrations] PRIMARY KEY ([MigrationId])
);
The specified schema name "{schema}" either does not exist or you do not have permission to use it.
Since I am working with a M1 chip laptop, I am running locally the database using azure-sql-edge
docker image and initialized with go-sqlcmd
using these SQL scripts (I have to split the commands in more scripts, otherwise not all objects are created):
USE master
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = N'$(Name)')
BEGIN
CREATE DATABASE $(Name)
END
---
USE $(Name)
IF NOT EXISTS (SELECT name FROM sys.schemas WHERE name = N'$(Schema)')
BEGIN
EXEC sys.sp_executesql N'CREATE SCHEMA [$(Schema)] AUTHORIZATION [dbo]'
END
IF NOT EXISTS(SELECT principal_id FROM sys.server_principals WHERE name = '$(Login)') BEGIN
CREATE LOGIN $(Login)
WITH PASSWORD = '$(Password)'
END
IF NOT EXISTS(SELECT principal_id FROM sys.database_principals WHERE name = '$(User)') BEGIN
CREATE USER $(User) FOR LOGIN $(Login)
END
---
USE $(Name)
GRANT CREATE TABLE TO $(User);
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA :: [$(Schema)] TO $(User);
I can connect to the database via Azure Data Studio using the created login and I see database and schema but EF can't create the migrations table with the same login.
More info:
Startup.cs
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>();
Console.WriteLine(databaseConfig.ConnectionString);
services.AddDbContext<TipJarContext>(
options => options.UseSqlServer(databaseConfig.ConnectionString)
);
}
}
DatabaseConfig.cs
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 Login { get; set; }
public string Password { get; set; }
public string ConnectionString
{
get { return $"Server={Host},{Port};Database={Name};User Id={Login};Password={Password}"; }
}
}
Context
public class TipJarContext : DbContext
{
private readonly DatabaseConfig databaseConfig;
public DbSet<User> Users { get; set; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public
{ProjectName}
TipJarContext(DbContextOptions<TipJarContext> options, DatabaseConfig databaseConfig)
: base(options)
{
this.databaseConfig = databaseConfig;
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(
databaseConfig.ConnectionString,
x => x.MigrationsHistoryTable("Migrations", databaseConfig.Schema)
);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema(databaseConfig.Schema);
}
}
User.cs
public class User : EntityBase
{
[Required]
[MaxLength(255)]
public string FirstName { get; set; }
[Required]
[MaxLength(255)]
public string LastName { get; set; }
[Required]
[MaxLength(255)]
public string Email { get; set; }
[Required]
public byte[] Password { get; set; }
[Required]
public byte[] Salt { get; set; }
}
EntityBase
just contains audit columns.
This is the printed connection string when I try to update the database:
Server=localhost,1433;Database=TipJar;User Id=TipJarLogin;Password=***
The configuration is correctly read from appconfig.json
.