Chỉ định phiên bản máy chủ Azure SQL trong EF Core mà không phá vỡ sự phát triển cục bộ


9

Entity Framework Core đã giới thiệu các phương thức HasServiceTierHasPerformanceLevel để thay đổi phiên bản của máy chủ Azure SQL. Bạn có thể sử dụng chúng trong OnModelCreating như thế này:

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

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

Nếu bạn sử dụng Thêm di chuyển Thêm di chuyển, bạn sẽ có một di chuyển như thế này:

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

Điều này có vẻ hoạt động tốt nhưng khi tôi cố gắng áp dụng di chuyển này cho DB không Azure cục bộ cho mục đích phát triển, tôi gặp phải lỗi sau:

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

Tôi giả sử các lệnh không hợp lệ cho các DB không phải Azure. Vì vậy, câu hỏi là: Làm thế nào tôi có thể ngăn chặn các lệnh này được thực thi trên các DB không phải Azure?


Di cư của bạn chạy như thế nào? Nếu bằng mã, bạn có thể bật ASPNETCORE_ENVIRONMENT docs.microsoft.com/en-us/aspnet/core/fundamentals/ trộm
Patrick Goode

@Patrick Goode sẽ chỉ cho phép tôi vô hiệu hóa di chuyển cho db cục bộ, phải không? Tôi muốn tất cả các di chuyển để chạy ngoại trừ cái này. Một giải pháp sẽ là làm cho nội dung di chuyển phụ thuộc vào biến cấu hình. Tôi chỉ tự hỏi nếu có một giải pháp thanh lịch hơn.
Tim Pohlmann

1
Thay vì lãng phí tiền thưởng, bạn thực sự nên đăng nó lên EF Core Issue Tracker vì đây là lỗi / vấn đề - nguồn của họ . Như bạn có thể thấy, có những khốiđiều kiện cho những thứ khác, nhưng không phải cho khối này. Tất nhiên bạn có thể thay thế lớp của họ bằng tùy chỉnh, nhưng bạn phải sao chép / dán / sửa đổi toàn bộ phương thức.
Ivan Stoev

1
Tôi chỉ thấy rằng bạn đã làm điều đó - # 20682 . Chúc may mắn.
Ivan Stoev

1
@IvanStoev đó là một cái nhìn sâu sắc thú vị trong mã nguồn. Cảm ơn đã đào nó lên.
Tim Pohlmann

Câu trả lời:


2

Nhóm EF Core hiện đã biết về vấn đề này và đã thêm nó vào hồ sơ tồn đọng của họ: https://github.com/dotnet/efcore/issues/20682

Trong khi đó, cách giải quyết chính thức được đề xuất như thế này:

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

Tôi đã sửa đổi nó để làm việc mà không biết tên cơ sở dữ liệu hiện tại:

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

Tất nhiên EDITIONSERVICE_OBJECTIVEkhông được hỗ trợ cho Cơ sở dữ liệu SQL không Azure.

Bạn chỉ cần chạy các lệnh của mình cho cơ sở dữ liệu Azure. Đối với các loại máy chủ SQL khác, bạn cần bỏ lỡ việc thực thi mã của mình.

Tôi đề nghị phát hiện SQL Server Edition trước khi chạy mã của bạn.

Đối với mục đích này, bạn có thể thêm phương thức mở rộng:

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

Và bên trong OnModelCreatingphương thức của bạn, bạn có thể sử dụng mã tiếp theo:

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

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

Tôi nghi ngờ điều này sẽ làm việc. Mã gây ra sự cố nằm trong di chuyển không phải trong OnModelCreating. Tôi đã cân nhắc việc sử dụng một cái gì đó tương tự trong quá trình di chuyển nhưng điều đó có vẻ hơi khó khăn, đó là lý do tại sao tôi mở câu hỏi này.
Tim Pohlmann

0

Điều này cảm thấy rất sai nhưng nó hoạt động:

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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.