Geben Sie die Azure SQL Server-Edition in EF Core an, ohne die lokale Entwicklung zu unterbrechen

9

Entity Framework Core führte die Methoden HasServiceTier und HasPerformanceLevel ein , um die Edition eines Azure SQL-Servers zu ändern. Sie können sie in OnModelCreating folgendermaßen verwenden :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.HasServiceTier("Basic");
    modelBuilder.HasPerformanceLevel("Basic");
}

Wenn Sie Add-Migration Add-Migration verwenden, erhalten Sie eine Migration wie folgt:

public partial class ChangedDatabaseServiceTierToBasic : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AlterDatabase()
            .Annotation("SqlServer:EditionOptions", "EDITION = 'Basic', SERVICE_OBJECTIVE = 'Basic'");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AlterDatabase()
            .OldAnnotation("SqlServer:EditionOptions", "EDITION = 'Basic', SERVICE_OBJECTIVE = 'Basic'");
    }
}

Dies scheint gut zu funktionieren, aber wenn ich versuche, diese Migration für Entwicklungszwecke auf eine lokale Nicht-Azure-Datenbank anzuwenden, wird der folgende Fehler angezeigt:

Microsoft.EntityFrameworkCore.Migrations[20402]
      Applying migration '20200413102908_ChangedDatabaseServiceTierToBasic'.
Applying migration '20200413102908_ChangedDatabaseServiceTierToBasic'.
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      BEGIN
      DECLARE @db_name NVARCHAR(MAX) = DB_NAME();
      EXEC(N'ALTER DATABASE ' + @db_name + ' MODIFY ( 
      EDITION = ''Basic'', SERVICE_OBJECTIVE = ''Basic'' );');
      END
Failed executing DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
BEGIN
DECLARE @db_name NVARCHAR(MAX) = DB_NAME();
EXEC(N'ALTER DATABASE ' + @db_name + ' MODIFY ( 
EDITION = ''Basic'', SERVICE_OBJECTIVE = ''Basic'' );');
END
Microsoft.Data.SqlClient.SqlException (0x80131904): Incorrect syntax near '.'.
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean isAsync, Int32 timeout, Boolean asyncWrite)
   at Microsoft.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String methodName)
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
ClientConnectionId:d9f92b81-9916-48ee-9686-6d0f567ab86f
Error Number:102,State:1,Class:15
Incorrect syntax near '.'.

Ich gehe davon aus, dass die Befehle für Nicht-Azure-DBs nicht gültig sind. Die Frage lautet also: Wie kann ich verhindern, dass diese Befehle auf Nicht-Azure-DBs ausgeführt werden?

Tim Pohlmann
quelle
Wie laufen Ihre Migrationen ab? Wenn im Code, können Sie ASPNETCORE_ENVIRONMENT docs.microsoft.com/en-us/aspnet/core/fundamentals/…
Patrick Goode
@PatrickGoode, mit dem ich nur Migrationen für die lokale Datenbank vollständig deaktivieren kann, oder? Ich möchte, dass alle Migrationen außer dieser ausgeführt werden. Eine Lösung wäre, den Inhalt der Migration von einer Konfigurationsvariablen abhängig zu machen. Ich habe mich nur gefragt, ob es eine elegantere Lösung gibt.
Tim Pohlmann
1
Anstatt Kopfgelder zu verschwenden, sollten Sie es wirklich an EF Core Issue Tracker senden, da es sich um deren Fehler- / Problemquelle handelt . Wie Sie sehen können, gibt es bedingte Blöcke für andere Dinge, aber nicht für diesen. Natürlich können Sie ihre Klasse durch benutzerdefinierte ersetzen, aber Sie müssen die gesamte Methode kopieren / einfügen / ändern.
Ivan Stoev
1
Ich habe gerade gesehen, dass du das schon getan hast - # 20682 . Viel Glück.
Ivan Stoev
1
@IvanStoev das ist ein interessanter Einblick in den Quellcode. Danke, dass du es ausgegraben hast.
Tim Pohlmann

Antworten:

2

Das EF Core-Team ist sich des Problems jetzt bewusst und hat es seinem Backlog hinzugefügt: https://github.com/dotnet/efcore/issues/20682

In der Zwischenzeit sieht die offiziell empfohlene Problemumgehung folgendermaßen aus:

migrationBuilder.Sql(@"IF SERVERPROPERTY('EngineEdition') = 5
EXEC(N'ALTER DATABASE [ThreeOne.SomeDbContext] MODIFY (EDITION = ''Basic'',  SERVICE_OBJECTIVE = ''Basic'' );');
");

Ich habe es so geändert, dass es funktioniert, ohne den aktuellen Datenbanknamen zu kennen:

migrationBuilder.Sql
(
@"declare @dbname varchar(100)
set @dbname=quotename(db_name())
IF SERVERPROPERTY('EngineEdition') = 5
EXEC(N'ALTER DATABASE '+@dbname+' MODIFY (EDITION = ''Basic'', SERVICE_OBJECTIVE = ''Basic'' );');"
);
Tim Pohlmann
quelle
0

Natürlich EDITIONund SERVICE_OBJECTIVEwerden für Nicht-Azure SQL-Datenbanken nicht unterstützt.

Sie müssen Ihre Befehle nur für die Azure-Datenbank ausführen. Bei anderen SQL Server-Typen müssen Sie die Ausführung Ihres Codes verpassen.

Ich empfehle, SQL Server Edition zu erkennen, bevor Sie Ihren Code ausführen.

Zu diesem Zweck können Sie eine Erweiterungsmethode hinzufügen:

public static class DatabaseFacadeExtensions
{
    public static bool IsSqlAzure(this DatabaseFacade database)
    {
        var parameter = new SqlParameter("edition", SqlDbType.NVarChar)
        {
            Size = 128,
            Direction = ParameterDirection.Output
        };

        database.ExecuteSqlCommand("SELECT @edition = CAST(SERVERPROPERTY('Edition') AS NVARCHAR)", parameter);

        var edition = parameter.Value.ToString();

        return edition.Equals("SQL Azure", StringComparison.OrdinalIgnoreCase);
    }
}

Und innerhalb Ihrer OnModelCreatingMethode können Sie den nächsten Code verwenden:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    if (Database.IsSqlAzure())
    {
        modelBuilder.HasServiceTier("Basic");
        modelBuilder.HasPerformanceLevel("Basic");
    }
}
Alexander I.
quelle
Ich bezweifle, dass das funktionieren wird. Der Code, der Probleme verursacht, befindet sich in der Migration, nicht in OnModelCreating. Ich habe überlegt, so etwas in der Migration selbst zu verwenden, aber das scheint ein bisschen wackelig zu sein, weshalb ich diese Frage geöffnet habe.
Tim Pohlmann
0

Das fühlt sich sehr falsch an, aber es funktioniert:

public partial class ChangedDatabaseServiceTierToBasic : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        if (IsHostedInAzure())
        {
            migrationBuilder.AlterDatabase()
                .Annotation("SqlServer:EditionOptions", "EDITION = 'Basic', SERVICE_OBJECTIVE = 'Basic'");
        }
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        if (IsHostedInAzure())
        {
            migrationBuilder.AlterDatabase()
                .OldAnnotation("SqlServer:EditionOptions", "EDITION = 'Basic', SERVICE_OBJECTIVE = 'Basic'");
        }
    }

    private static bool IsHostedInAzure()
    {
        var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true)
            .Build();

        var isHostedInAzureConfig = config["DatabaseSettings:IsHostedInAzure"];
        var setEdition = bool.TryParse(isHostedInAzureConfig, out var isHostedInAzure) && isHostedInAzure;
        return setEdition;
    }
}
Tim Pohlmann
quelle