ระบุรุ่นเซิร์ฟเวอร์ Azure SQL ใน EF Core โดยไม่หยุดยั้งการพัฒนาในท้องถิ่น


10

Entity Framework Core แนะนำวิธีการHasServiceTierและHasPerformanceLevelเพื่อเปลี่ยนรุ่นของเซิร์ฟเวอร์ Azure SQL คุณสามารถใช้พวกเขาในOnModelCreatingเช่นนี้:

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

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

หากคุณใช้ Add-Migration Add-Migrationคุณจะได้รับการย้ายข้อมูลดังนี้:

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'");
    }
}

ดูเหมือนว่าจะทำงานได้ดี แต่เมื่อฉันพยายามใช้การโยกย้ายนี้กับฐานข้อมูลที่ไม่ใช่ Azure ในพื้นที่เพื่อการพัฒนาฉันได้รับข้อผิดพลาดต่อไปนี้:

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 '.'.

ฉันถือว่าคำสั่งไม่ถูกต้องสำหรับฐานข้อมูลที่ไม่ใช่ Azure ดังนั้นคำถามคือ: ฉันจะป้องกันคำสั่งเหล่านี้เพื่อดำเนินการในฐานข้อมูลที่ไม่ใช่ Azure ได้อย่างไร


การย้ายข้อมูลของคุณทำงานอย่างไร หากอยู่ในรหัสคุณสามารถเปิดใช้ ASPNETCORE_ENVIRONMENT docs.microsoft.com/en-us/aspnet/core/fundamentals/ …
Patrick Goode

@PatrickGoode ที่จะอนุญาตให้ฉันปิดใช้งานการย้ายข้อมูลสำหรับฐานข้อมูลในเครื่องทั้งหมดใช่ไหม ฉันต้องการให้การโยกย้ายทั้งหมดทำงานยกเว้นอันนี้ ทางออกหนึ่งคือการทำให้เนื้อหาของการย้ายข้อมูลขึ้นอยู่กับตัวแปรการตั้งค่า ฉันแค่สงสัยว่ามีทางออกที่สง่างามกว่านี้หรือไม่
Tim Pohlmann

1
แทนที่จะเสียโปรดปรานคุณควรโพสต์ไป EF แกนติดตามปัญหาเพราะมันเป็นข้อผิดพลาด / ปัญหาของพวกเขา - แหล่งที่มา อย่างที่คุณเห็นมีบล็อกที่มีเงื่อนไขสำหรับสิ่งอื่น ๆ แต่ไม่ใช่สำหรับบล็อกนี้ แน่นอนว่าคุณสามารถแทนที่ชั้นเรียนด้วยแบบกำหนดเองได้ แต่คุณต้องคัดลอก / วาง / ปรับเปลี่ยนวิธีการทั้งหมด
Ivan Stoev

1
ฉันเพิ่งเห็นว่าคุณไม่ได้อยู่แล้วว่า - # 20682 โชคดี.
Ivan Stoev

1
@IvanStoev เป็นข้อมูลเชิงลึกที่น่าสนใจในซอร์สโค้ด ขอบคุณที่ขุดมันขึ้นมา
Tim Pohlmann

คำตอบ:


3

ทีม EF Core ทราบถึงปัญหานี้แล้วและเพิ่มลงใน Backlog: https://github.com/dotnet/efcore/issues/20682

ในขณะเดียวกันวิธีแก้ปัญหาที่แนะนำอย่างเป็นทางการมีลักษณะดังนี้

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

ฉันแก้ไขให้ทำงานโดยไม่ทราบชื่อฐานข้อมูลปัจจุบัน:

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'' );');"
);

0

แน่นอนEDITIONและSERVICE_OBJECTIVEไม่ได้รับการสนับสนุนสำหรับฐานข้อมูล Azure SQL ที่ไม่ใช่

คุณต้องเรียกใช้คำสั่งของคุณสำหรับฐานข้อมูล Azure เท่านั้น สำหรับเซิร์ฟเวอร์ SQL ชนิดอื่นคุณต้องพลาดการเรียกใช้งานโค้ดของคุณ

ฉันขอแนะนำให้ตรวจสอบSQL Server Editionก่อนที่จะเรียกใช้รหัสของคุณ

เพื่อจุดประสงค์นี้คุณสามารถเพิ่มวิธีการขยาย:

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);
    }
}

และในOnModelCreatingวิธีการของคุณคุณสามารถใช้รหัสถัดไป:

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

    if (Database.IsSqlAzure())
    {
        modelBuilder.HasServiceTier("Basic");
        modelBuilder.HasPerformanceLevel("Basic");
    }
}

ฉันสงสัยว่านี่จะใช้งานได้ รหัสที่ทำให้เกิดปัญหาจะอยู่ในการโยกย้ายไม่ได้อยู่ใน OnModelCreating ฉันพิจารณาการใช้บางอย่างเช่นนั้นในการย้ายถิ่นฐานเอง แต่ดูเหมือนจะไม่เกะกะซึ่งเป็นสาเหตุที่ฉันเปิดคำถามนี้
Tim Pohlmann

0

สิ่งนี้รู้สึกผิดมาก แต่ใช้งานได้:

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;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.